diff options
132 files changed, 8981 insertions, 1234 deletions
diff --git a/Documentation/ABI/stable/sysfs-bus-vmbus b/Documentation/ABI/stable/sysfs-bus-vmbus index 0c9d9dcd2151..3eaffbb2d468 100644 --- a/Documentation/ABI/stable/sysfs-bus-vmbus +++ b/Documentation/ABI/stable/sysfs-bus-vmbus | |||
| @@ -1,25 +1,25 @@ | |||
| 1 | What: /sys/bus/vmbus/devices/vmbus_*/id | 1 | What: /sys/bus/vmbus/devices/<UUID>/id |
| 2 | Date: Jul 2009 | 2 | Date: Jul 2009 |
| 3 | KernelVersion: 2.6.31 | 3 | KernelVersion: 2.6.31 |
| 4 | Contact: K. Y. Srinivasan <kys@microsoft.com> | 4 | Contact: K. Y. Srinivasan <kys@microsoft.com> |
| 5 | Description: The VMBus child_relid of the device's primary channel | 5 | Description: The VMBus child_relid of the device's primary channel |
| 6 | Users: tools/hv/lsvmbus | 6 | Users: tools/hv/lsvmbus |
| 7 | 7 | ||
| 8 | What: /sys/bus/vmbus/devices/vmbus_*/class_id | 8 | What: /sys/bus/vmbus/devices/<UUID>/class_id |
| 9 | Date: Jul 2009 | 9 | Date: Jul 2009 |
| 10 | KernelVersion: 2.6.31 | 10 | KernelVersion: 2.6.31 |
| 11 | Contact: K. Y. Srinivasan <kys@microsoft.com> | 11 | Contact: K. Y. Srinivasan <kys@microsoft.com> |
| 12 | Description: The VMBus interface type GUID of the device | 12 | Description: The VMBus interface type GUID of the device |
| 13 | Users: tools/hv/lsvmbus | 13 | Users: tools/hv/lsvmbus |
| 14 | 14 | ||
| 15 | What: /sys/bus/vmbus/devices/vmbus_*/device_id | 15 | What: /sys/bus/vmbus/devices/<UUID>/device_id |
| 16 | Date: Jul 2009 | 16 | Date: Jul 2009 |
| 17 | KernelVersion: 2.6.31 | 17 | KernelVersion: 2.6.31 |
| 18 | Contact: K. Y. Srinivasan <kys@microsoft.com> | 18 | Contact: K. Y. Srinivasan <kys@microsoft.com> |
| 19 | Description: The VMBus interface instance GUID of the device | 19 | Description: The VMBus interface instance GUID of the device |
| 20 | Users: tools/hv/lsvmbus | 20 | Users: tools/hv/lsvmbus |
| 21 | 21 | ||
| 22 | What: /sys/bus/vmbus/devices/vmbus_*/channel_vp_mapping | 22 | What: /sys/bus/vmbus/devices/<UUID>/channel_vp_mapping |
| 23 | Date: Jul 2015 | 23 | Date: Jul 2015 |
| 24 | KernelVersion: 4.2.0 | 24 | KernelVersion: 4.2.0 |
| 25 | Contact: K. Y. Srinivasan <kys@microsoft.com> | 25 | Contact: K. Y. Srinivasan <kys@microsoft.com> |
| @@ -28,112 +28,112 @@ Description: The mapping of which primary/sub channels are bound to which | |||
| 28 | Format: <channel's child_relid:the bound cpu's number> | 28 | Format: <channel's child_relid:the bound cpu's number> |
| 29 | Users: tools/hv/lsvmbus | 29 | Users: tools/hv/lsvmbus |
| 30 | 30 | ||
| 31 | What: /sys/bus/vmbus/devices/vmbus_*/device | 31 | What: /sys/bus/vmbus/devices/<UUID>/device |
| 32 | Date: Dec. 2015 | 32 | Date: Dec. 2015 |
| 33 | KernelVersion: 4.5 | 33 | KernelVersion: 4.5 |
| 34 | Contact: K. Y. Srinivasan <kys@microsoft.com> | 34 | Contact: K. Y. Srinivasan <kys@microsoft.com> |
| 35 | Description: The 16 bit device ID of the device | 35 | Description: The 16 bit device ID of the device |
| 36 | Users: tools/hv/lsvmbus and user level RDMA libraries | 36 | Users: tools/hv/lsvmbus and user level RDMA libraries |
| 37 | 37 | ||
| 38 | What: /sys/bus/vmbus/devices/vmbus_*/vendor | 38 | What: /sys/bus/vmbus/devices/<UUID>/vendor |
| 39 | Date: Dec. 2015 | 39 | Date: Dec. 2015 |
| 40 | KernelVersion: 4.5 | 40 | KernelVersion: 4.5 |
| 41 | Contact: K. Y. Srinivasan <kys@microsoft.com> | 41 | Contact: K. Y. Srinivasan <kys@microsoft.com> |
| 42 | Description: The 16 bit vendor ID of the device | 42 | Description: The 16 bit vendor ID of the device |
| 43 | Users: tools/hv/lsvmbus and user level RDMA libraries | 43 | Users: tools/hv/lsvmbus and user level RDMA libraries |
| 44 | 44 | ||
| 45 | What: /sys/bus/vmbus/devices/vmbus_*/channels/NN | 45 | What: /sys/bus/vmbus/devices/<UUID>/channels/<N> |
| 46 | Date: September. 2017 | 46 | Date: September. 2017 |
| 47 | KernelVersion: 4.14 | 47 | KernelVersion: 4.14 |
| 48 | Contact: Stephen Hemminger <sthemmin@microsoft.com> | 48 | Contact: Stephen Hemminger <sthemmin@microsoft.com> |
| 49 | Description: Directory for per-channel information | 49 | Description: Directory for per-channel information |
| 50 | NN is the VMBUS relid associtated with the channel. | 50 | NN is the VMBUS relid associtated with the channel. |
| 51 | 51 | ||
| 52 | What: /sys/bus/vmbus/devices/vmbus_*/channels/NN/cpu | 52 | What: /sys/bus/vmbus/devices/<UUID>/channels/<N>/cpu |
| 53 | Date: September. 2017 | 53 | Date: September. 2017 |
| 54 | KernelVersion: 4.14 | 54 | KernelVersion: 4.14 |
| 55 | Contact: Stephen Hemminger <sthemmin@microsoft.com> | 55 | Contact: Stephen Hemminger <sthemmin@microsoft.com> |
| 56 | Description: VCPU (sub)channel is affinitized to | 56 | Description: VCPU (sub)channel is affinitized to |
| 57 | Users: tools/hv/lsvmbus and other debugging tools | 57 | Users: tools/hv/lsvmbus and other debugging tools |
| 58 | 58 | ||
| 59 | What: /sys/bus/vmbus/devices/vmbus_*/channels/NN/cpu | 59 | What: /sys/bus/vmbus/devices/<UUID>/channels/<N>/cpu |
| 60 | Date: September. 2017 | 60 | Date: September. 2017 |
| 61 | KernelVersion: 4.14 | 61 | KernelVersion: 4.14 |
| 62 | Contact: Stephen Hemminger <sthemmin@microsoft.com> | 62 | Contact: Stephen Hemminger <sthemmin@microsoft.com> |
| 63 | Description: VCPU (sub)channel is affinitized to | 63 | Description: VCPU (sub)channel is affinitized to |
| 64 | Users: tools/hv/lsvmbus and other debugging tools | 64 | Users: tools/hv/lsvmbus and other debugging tools |
| 65 | 65 | ||
| 66 | What: /sys/bus/vmbus/devices/vmbus_*/channels/NN/in_mask | 66 | What: /sys/bus/vmbus/devices/<UUID>/channels/<N>/in_mask |
| 67 | Date: September. 2017 | 67 | Date: September. 2017 |
| 68 | KernelVersion: 4.14 | 68 | KernelVersion: 4.14 |
| 69 | Contact: Stephen Hemminger <sthemmin@microsoft.com> | 69 | Contact: Stephen Hemminger <sthemmin@microsoft.com> |
| 70 | Description: Host to guest channel interrupt mask | 70 | Description: Host to guest channel interrupt mask |
| 71 | Users: Debugging tools | 71 | Users: Debugging tools |
| 72 | 72 | ||
| 73 | What: /sys/bus/vmbus/devices/vmbus_*/channels/NN/latency | 73 | What: /sys/bus/vmbus/devices/<UUID>/channels/<N>/latency |
| 74 | Date: September. 2017 | 74 | Date: September. 2017 |
| 75 | KernelVersion: 4.14 | 75 | KernelVersion: 4.14 |
| 76 | Contact: Stephen Hemminger <sthemmin@microsoft.com> | 76 | Contact: Stephen Hemminger <sthemmin@microsoft.com> |
| 77 | Description: Channel signaling latency | 77 | Description: Channel signaling latency |
| 78 | Users: Debugging tools | 78 | Users: Debugging tools |
| 79 | 79 | ||
| 80 | What: /sys/bus/vmbus/devices/vmbus_*/channels/NN/out_mask | 80 | What: /sys/bus/vmbus/devices/<UUID>/channels/<N>/out_mask |
| 81 | Date: September. 2017 | 81 | Date: September. 2017 |
| 82 | KernelVersion: 4.14 | 82 | KernelVersion: 4.14 |
| 83 | Contact: Stephen Hemminger <sthemmin@microsoft.com> | 83 | Contact: Stephen Hemminger <sthemmin@microsoft.com> |
| 84 | Description: Guest to host channel interrupt mask | 84 | Description: Guest to host channel interrupt mask |
| 85 | Users: Debugging tools | 85 | Users: Debugging tools |
| 86 | 86 | ||
| 87 | What: /sys/bus/vmbus/devices/vmbus_*/channels/NN/pending | 87 | What: /sys/bus/vmbus/devices/<UUID>/channels/<N>/pending |
| 88 | Date: September. 2017 | 88 | Date: September. 2017 |
| 89 | KernelVersion: 4.14 | 89 | KernelVersion: 4.14 |
| 90 | Contact: Stephen Hemminger <sthemmin@microsoft.com> | 90 | Contact: Stephen Hemminger <sthemmin@microsoft.com> |
| 91 | Description: Channel interrupt pending state | 91 | Description: Channel interrupt pending state |
| 92 | Users: Debugging tools | 92 | Users: Debugging tools |
| 93 | 93 | ||
| 94 | What: /sys/bus/vmbus/devices/vmbus_*/channels/NN/read_avail | 94 | What: /sys/bus/vmbus/devices/<UUID>/channels/<N>/read_avail |
| 95 | Date: September. 2017 | 95 | Date: September. 2017 |
| 96 | KernelVersion: 4.14 | 96 | KernelVersion: 4.14 |
| 97 | Contact: Stephen Hemminger <sthemmin@microsoft.com> | 97 | Contact: Stephen Hemminger <sthemmin@microsoft.com> |
| 98 | Description: Bytes available to read | 98 | Description: Bytes available to read |
| 99 | Users: Debugging tools | 99 | Users: Debugging tools |
| 100 | 100 | ||
| 101 | What: /sys/bus/vmbus/devices/vmbus_*/channels/NN/write_avail | 101 | What: /sys/bus/vmbus/devices/<UUID>/channels/<N>/write_avail |
| 102 | Date: September. 2017 | 102 | Date: September. 2017 |
| 103 | KernelVersion: 4.14 | 103 | KernelVersion: 4.14 |
| 104 | Contact: Stephen Hemminger <sthemmin@microsoft.com> | 104 | Contact: Stephen Hemminger <sthemmin@microsoft.com> |
| 105 | Description: Bytes available to write | 105 | Description: Bytes available to write |
| 106 | Users: Debugging tools | 106 | Users: Debugging tools |
| 107 | 107 | ||
| 108 | What: /sys/bus/vmbus/devices/vmbus_*/channels/NN/events | 108 | What: /sys/bus/vmbus/devices/<UUID>/channels/<N>/events |
| 109 | Date: September. 2017 | 109 | Date: September. 2017 |
| 110 | KernelVersion: 4.14 | 110 | KernelVersion: 4.14 |
| 111 | Contact: Stephen Hemminger <sthemmin@microsoft.com> | 111 | Contact: Stephen Hemminger <sthemmin@microsoft.com> |
| 112 | Description: Number of times we have signaled the host | 112 | Description: Number of times we have signaled the host |
| 113 | Users: Debugging tools | 113 | Users: Debugging tools |
| 114 | 114 | ||
| 115 | What: /sys/bus/vmbus/devices/vmbus_*/channels/NN/interrupts | 115 | What: /sys/bus/vmbus/devices/<UUID>/channels/<N>/interrupts |
| 116 | Date: September. 2017 | 116 | Date: September. 2017 |
| 117 | KernelVersion: 4.14 | 117 | KernelVersion: 4.14 |
| 118 | Contact: Stephen Hemminger <sthemmin@microsoft.com> | 118 | Contact: Stephen Hemminger <sthemmin@microsoft.com> |
| 119 | Description: Number of times we have taken an interrupt (incoming) | 119 | Description: Number of times we have taken an interrupt (incoming) |
| 120 | Users: Debugging tools | 120 | Users: Debugging tools |
| 121 | 121 | ||
| 122 | What: /sys/bus/vmbus/devices/vmbus_*/channels/NN/subchannel_id | 122 | What: /sys/bus/vmbus/devices/<UUID>/channels/<N>/subchannel_id |
| 123 | Date: January. 2018 | 123 | Date: January. 2018 |
| 124 | KernelVersion: 4.16 | 124 | KernelVersion: 4.16 |
| 125 | Contact: Stephen Hemminger <sthemmin@microsoft.com> | 125 | Contact: Stephen Hemminger <sthemmin@microsoft.com> |
| 126 | Description: Subchannel ID associated with VMBUS channel | 126 | Description: Subchannel ID associated with VMBUS channel |
| 127 | Users: Debugging tools and userspace drivers | 127 | Users: Debugging tools and userspace drivers |
| 128 | 128 | ||
| 129 | What: /sys/bus/vmbus/devices/vmbus_*/channels/NN/monitor_id | 129 | What: /sys/bus/vmbus/devices/<UUID>/channels/<N>/monitor_id |
| 130 | Date: January. 2018 | 130 | Date: January. 2018 |
| 131 | KernelVersion: 4.16 | 131 | KernelVersion: 4.16 |
| 132 | Contact: Stephen Hemminger <sthemmin@microsoft.com> | 132 | Contact: Stephen Hemminger <sthemmin@microsoft.com> |
| 133 | Description: Monitor bit associated with channel | 133 | Description: Monitor bit associated with channel |
| 134 | Users: Debugging tools and userspace drivers | 134 | Users: Debugging tools and userspace drivers |
| 135 | 135 | ||
| 136 | What: /sys/bus/vmbus/devices/vmbus_*/channels/NN/ring | 136 | What: /sys/bus/vmbus/devices/<UUID>/channels/<N>/ring |
| 137 | Date: January. 2018 | 137 | Date: January. 2018 |
| 138 | KernelVersion: 4.16 | 138 | KernelVersion: 4.16 |
| 139 | Contact: Stephen Hemminger <sthemmin@microsoft.com> | 139 | Contact: Stephen Hemminger <sthemmin@microsoft.com> |
diff --git a/Documentation/devicetree/bindings/fpga/lattice-machxo2-spi.txt b/Documentation/devicetree/bindings/fpga/lattice-machxo2-spi.txt new file mode 100644 index 000000000000..a8c362eb160c --- /dev/null +++ b/Documentation/devicetree/bindings/fpga/lattice-machxo2-spi.txt | |||
| @@ -0,0 +1,29 @@ | |||
| 1 | Lattice MachXO2 Slave SPI FPGA Manager | ||
| 2 | |||
| 3 | Lattice MachXO2 FPGAs support a method of loading the bitstream over | ||
| 4 | 'slave SPI' interface. | ||
| 5 | |||
| 6 | See 'MachXO2ProgrammingandConfigurationUsageGuide.pdf' on www.latticesemi.com | ||
| 7 | |||
| 8 | Required properties: | ||
| 9 | - compatible: should contain "lattice,machxo2-slave-spi" | ||
| 10 | - reg: spi chip select of the FPGA | ||
| 11 | |||
| 12 | Example for full FPGA configuration: | ||
| 13 | |||
| 14 | fpga-region0 { | ||
| 15 | compatible = "fpga-region"; | ||
| 16 | fpga-mgr = <&fpga_mgr_spi>; | ||
| 17 | #address-cells = <0x1>; | ||
| 18 | #size-cells = <0x1>; | ||
| 19 | }; | ||
| 20 | |||
| 21 | spi1: spi@2000 { | ||
| 22 | ... | ||
| 23 | |||
| 24 | fpga_mgr_spi: fpga-mgr@0 { | ||
| 25 | compatible = "lattice,machxo2-slave-spi"; | ||
| 26 | spi-max-frequency = <8000000>; | ||
| 27 | reg = <0>; | ||
| 28 | }; | ||
| 29 | }; | ||
diff --git a/Documentation/devicetree/bindings/nvmem/zii,rave-sp-eeprom.txt b/Documentation/devicetree/bindings/nvmem/zii,rave-sp-eeprom.txt new file mode 100644 index 000000000000..d5e22fc67d66 --- /dev/null +++ b/Documentation/devicetree/bindings/nvmem/zii,rave-sp-eeprom.txt | |||
| @@ -0,0 +1,40 @@ | |||
| 1 | Zodiac Inflight Innovations RAVE EEPROM Bindings | ||
| 2 | |||
| 3 | RAVE SP EEPROM device is a "MFD cell" device exposing physical EEPROM | ||
| 4 | attached to RAVE Supervisory Processor. It is expected that its Device | ||
| 5 | Tree node is specified as a child of the node corresponding to the | ||
| 6 | parent RAVE SP device (as documented in | ||
| 7 | Documentation/devicetree/bindings/mfd/zii,rave-sp.txt) | ||
| 8 | |||
| 9 | Required properties: | ||
| 10 | |||
| 11 | - compatible: Should be "zii,rave-sp-eeprom" | ||
| 12 | |||
| 13 | Optional properties: | ||
| 14 | |||
| 15 | - zii,eeprom-name: Unique EEPROM identifier describing its function in the | ||
| 16 | system. Will be used as created NVMEM deivce's name. | ||
| 17 | |||
| 18 | Data cells: | ||
| 19 | |||
| 20 | Data cells are child nodes of eerpom node, bindings for which are | ||
| 21 | documented in Documentation/bindings/nvmem/nvmem.txt | ||
| 22 | |||
| 23 | Example: | ||
| 24 | |||
| 25 | rave-sp { | ||
| 26 | compatible = "zii,rave-sp-rdu1"; | ||
| 27 | current-speed = <38400>; | ||
| 28 | |||
| 29 | eeprom@a4 { | ||
| 30 | compatible = "zii,rave-sp-eeprom"; | ||
| 31 | reg = <0xa4 0x4000>; | ||
| 32 | #address-cells = <1>; | ||
| 33 | #size-cells = <1>; | ||
| 34 | zii,eeprom-name = "main-eeprom"; | ||
| 35 | |||
| 36 | wdt_timeout: wdt-timeout@81 { | ||
| 37 | reg = <0x81 2>; | ||
| 38 | }; | ||
| 39 | }; | ||
| 40 | } | ||
diff --git a/Documentation/driver-api/fpga/fpga-bridge.rst b/Documentation/driver-api/fpga/fpga-bridge.rst new file mode 100644 index 000000000000..2c2aaca894bf --- /dev/null +++ b/Documentation/driver-api/fpga/fpga-bridge.rst | |||
| @@ -0,0 +1,49 @@ | |||
| 1 | FPGA Bridge | ||
| 2 | =========== | ||
| 3 | |||
| 4 | API to implement a new FPGA bridge | ||
| 5 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
| 6 | |||
| 7 | .. kernel-doc:: include/linux/fpga/fpga-bridge.h | ||
| 8 | :functions: fpga_bridge | ||
| 9 | |||
| 10 | .. kernel-doc:: include/linux/fpga/fpga-bridge.h | ||
| 11 | :functions: fpga_bridge_ops | ||
| 12 | |||
| 13 | .. kernel-doc:: drivers/fpga/fpga-bridge.c | ||
| 14 | :functions: fpga_bridge_create | ||
| 15 | |||
| 16 | .. kernel-doc:: drivers/fpga/fpga-bridge.c | ||
| 17 | :functions: fpga_bridge_free | ||
| 18 | |||
| 19 | .. kernel-doc:: drivers/fpga/fpga-bridge.c | ||
| 20 | :functions: fpga_bridge_register | ||
| 21 | |||
| 22 | .. kernel-doc:: drivers/fpga/fpga-bridge.c | ||
| 23 | :functions: fpga_bridge_unregister | ||
| 24 | |||
| 25 | API to control an FPGA bridge | ||
| 26 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
| 27 | |||
| 28 | You probably won't need these directly. FPGA regions should handle this. | ||
| 29 | |||
| 30 | .. kernel-doc:: drivers/fpga/fpga-bridge.c | ||
| 31 | :functions: of_fpga_bridge_get | ||
| 32 | |||
| 33 | .. kernel-doc:: drivers/fpga/fpga-bridge.c | ||
| 34 | :functions: fpga_bridge_get | ||
| 35 | |||
| 36 | .. kernel-doc:: drivers/fpga/fpga-bridge.c | ||
| 37 | :functions: fpga_bridge_put | ||
| 38 | |||
| 39 | .. kernel-doc:: drivers/fpga/fpga-bridge.c | ||
| 40 | :functions: fpga_bridge_get_to_list | ||
| 41 | |||
| 42 | .. kernel-doc:: drivers/fpga/fpga-bridge.c | ||
| 43 | :functions: of_fpga_bridge_get_to_list | ||
| 44 | |||
| 45 | .. kernel-doc:: drivers/fpga/fpga-bridge.c | ||
| 46 | :functions: fpga_bridge_enable | ||
| 47 | |||
| 48 | .. kernel-doc:: drivers/fpga/fpga-bridge.c | ||
| 49 | :functions: fpga_bridge_disable | ||
diff --git a/Documentation/driver-api/fpga/fpga-mgr.rst b/Documentation/driver-api/fpga/fpga-mgr.rst new file mode 100644 index 000000000000..bcf2dd24e179 --- /dev/null +++ b/Documentation/driver-api/fpga/fpga-mgr.rst | |||
| @@ -0,0 +1,220 @@ | |||
| 1 | FPGA Manager | ||
| 2 | ============ | ||
| 3 | |||
| 4 | Overview | ||
| 5 | -------- | ||
| 6 | |||
| 7 | The FPGA manager core exports a set of functions for programming an FPGA with | ||
| 8 | an image. The API is manufacturer agnostic. All manufacturer specifics are | ||
| 9 | hidden away in a low level driver which registers a set of ops with the core. | ||
| 10 | The FPGA image data itself is very manufacturer specific, but for our purposes | ||
| 11 | it's just binary data. The FPGA manager core won't parse it. | ||
| 12 | |||
| 13 | The FPGA image to be programmed can be in a scatter gather list, a single | ||
| 14 | contiguous buffer, or a firmware file. Because allocating contiguous kernel | ||
| 15 | memory for the buffer should be avoided, users are encouraged to use a scatter | ||
| 16 | gather list instead if possible. | ||
| 17 | |||
| 18 | The particulars for programming the image are presented in a structure (struct | ||
| 19 | fpga_image_info). This struct contains parameters such as pointers to the | ||
| 20 | FPGA image as well as image-specific particulars such as whether the image was | ||
| 21 | built for full or partial reconfiguration. | ||
| 22 | |||
| 23 | How to support a new FPGA device | ||
| 24 | -------------------------------- | ||
| 25 | |||
| 26 | To add another FPGA manager, write a driver that implements a set of ops. The | ||
| 27 | probe function calls fpga_mgr_register(), such as:: | ||
| 28 | |||
| 29 | static const struct fpga_manager_ops socfpga_fpga_ops = { | ||
| 30 | .write_init = socfpga_fpga_ops_configure_init, | ||
| 31 | .write = socfpga_fpga_ops_configure_write, | ||
| 32 | .write_complete = socfpga_fpga_ops_configure_complete, | ||
| 33 | .state = socfpga_fpga_ops_state, | ||
| 34 | }; | ||
| 35 | |||
| 36 | static int socfpga_fpga_probe(struct platform_device *pdev) | ||
| 37 | { | ||
| 38 | struct device *dev = &pdev->dev; | ||
| 39 | struct socfpga_fpga_priv *priv; | ||
| 40 | struct fpga_manager *mgr; | ||
| 41 | int ret; | ||
| 42 | |||
| 43 | priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); | ||
| 44 | if (!priv) | ||
| 45 | return -ENOMEM; | ||
| 46 | |||
| 47 | /* | ||
| 48 | * do ioremaps, get interrupts, etc. and save | ||
| 49 | * them in priv | ||
| 50 | */ | ||
| 51 | |||
| 52 | mgr = fpga_mgr_create(dev, "Altera SOCFPGA FPGA Manager", | ||
| 53 | &socfpga_fpga_ops, priv); | ||
| 54 | if (!mgr) | ||
| 55 | return -ENOMEM; | ||
| 56 | |||
| 57 | platform_set_drvdata(pdev, mgr); | ||
| 58 | |||
| 59 | ret = fpga_mgr_register(mgr); | ||
| 60 | if (ret) | ||
| 61 | fpga_mgr_free(mgr); | ||
| 62 | |||
| 63 | return ret; | ||
| 64 | } | ||
| 65 | |||
| 66 | static int socfpga_fpga_remove(struct platform_device *pdev) | ||
| 67 | { | ||
| 68 | struct fpga_manager *mgr = platform_get_drvdata(pdev); | ||
| 69 | |||
| 70 | fpga_mgr_unregister(mgr); | ||
| 71 | |||
| 72 | return 0; | ||
| 73 | } | ||
| 74 | |||
| 75 | |||
| 76 | The ops will implement whatever device specific register writes are needed to | ||
| 77 | do the programming sequence for this particular FPGA. These ops return 0 for | ||
| 78 | success or negative error codes otherwise. | ||
| 79 | |||
| 80 | The programming sequence is:: | ||
| 81 | 1. .write_init | ||
| 82 | 2. .write or .write_sg (may be called once or multiple times) | ||
| 83 | 3. .write_complete | ||
| 84 | |||
| 85 | The .write_init function will prepare the FPGA to receive the image data. The | ||
| 86 | buffer passed into .write_init will be atmost .initial_header_size bytes long, | ||
| 87 | if the whole bitstream is not immediately available then the core code will | ||
| 88 | buffer up at least this much before starting. | ||
| 89 | |||
| 90 | The .write function writes a buffer to the FPGA. The buffer may be contain the | ||
| 91 | whole FPGA image or may be a smaller chunk of an FPGA image. In the latter | ||
| 92 | case, this function is called multiple times for successive chunks. This interface | ||
| 93 | is suitable for drivers which use PIO. | ||
| 94 | |||
| 95 | The .write_sg version behaves the same as .write except the input is a sg_table | ||
| 96 | scatter list. This interface is suitable for drivers which use DMA. | ||
| 97 | |||
| 98 | The .write_complete function is called after all the image has been written | ||
| 99 | to put the FPGA into operating mode. | ||
| 100 | |||
| 101 | The ops include a .state function which will read the hardware FPGA manager and | ||
| 102 | return a code of type enum fpga_mgr_states. It doesn't result in a change in | ||
| 103 | hardware state. | ||
| 104 | |||
| 105 | How to write an image buffer to a supported FPGA | ||
| 106 | ------------------------------------------------ | ||
| 107 | |||
| 108 | Some sample code:: | ||
| 109 | |||
| 110 | #include <linux/fpga/fpga-mgr.h> | ||
| 111 | |||
| 112 | struct fpga_manager *mgr; | ||
| 113 | struct fpga_image_info *info; | ||
| 114 | int ret; | ||
| 115 | |||
| 116 | /* | ||
| 117 | * Get a reference to FPGA manager. The manager is not locked, so you can | ||
| 118 | * hold onto this reference without it preventing programming. | ||
| 119 | * | ||
| 120 | * This example uses the device node of the manager. Alternatively, use | ||
| 121 | * fpga_mgr_get(dev) instead if you have the device. | ||
| 122 | */ | ||
| 123 | mgr = of_fpga_mgr_get(mgr_node); | ||
| 124 | |||
| 125 | /* struct with information about the FPGA image to program. */ | ||
| 126 | info = fpga_image_info_alloc(dev); | ||
| 127 | |||
| 128 | /* flags indicates whether to do full or partial reconfiguration */ | ||
| 129 | info->flags = FPGA_MGR_PARTIAL_RECONFIG; | ||
| 130 | |||
| 131 | /* | ||
| 132 | * At this point, indicate where the image is. This is pseudo-code; you're | ||
| 133 | * going to use one of these three. | ||
| 134 | */ | ||
| 135 | if (image is in a scatter gather table) { | ||
| 136 | |||
| 137 | info->sgt = [your scatter gather table] | ||
| 138 | |||
| 139 | } else if (image is in a buffer) { | ||
| 140 | |||
| 141 | info->buf = [your image buffer] | ||
| 142 | info->count = [image buffer size] | ||
| 143 | |||
| 144 | } else if (image is in a firmware file) { | ||
| 145 | |||
| 146 | info->firmware_name = devm_kstrdup(dev, firmware_name, GFP_KERNEL); | ||
| 147 | |||
| 148 | } | ||
| 149 | |||
| 150 | /* Get exclusive control of FPGA manager */ | ||
| 151 | ret = fpga_mgr_lock(mgr); | ||
| 152 | |||
| 153 | /* Load the buffer to the FPGA */ | ||
| 154 | ret = fpga_mgr_buf_load(mgr, &info, buf, count); | ||
| 155 | |||
| 156 | /* Release the FPGA manager */ | ||
| 157 | fpga_mgr_unlock(mgr); | ||
| 158 | fpga_mgr_put(mgr); | ||
| 159 | |||
| 160 | /* Deallocate the image info if you're done with it */ | ||
| 161 | fpga_image_info_free(info); | ||
| 162 | |||
| 163 | API for implementing a new FPGA Manager driver | ||
| 164 | ---------------------------------------------- | ||
| 165 | |||
| 166 | .. kernel-doc:: include/linux/fpga/fpga-mgr.h | ||
| 167 | :functions: fpga_manager | ||
| 168 | |||
| 169 | .. kernel-doc:: include/linux/fpga/fpga-mgr.h | ||
| 170 | :functions: fpga_manager_ops | ||
| 171 | |||
| 172 | .. kernel-doc:: drivers/fpga/fpga-mgr.c | ||
| 173 | :functions: fpga_mgr_create | ||
| 174 | |||
| 175 | .. kernel-doc:: drivers/fpga/fpga-mgr.c | ||
| 176 | :functions: fpga_mgr_free | ||
| 177 | |||
| 178 | .. kernel-doc:: drivers/fpga/fpga-mgr.c | ||
| 179 | :functions: fpga_mgr_register | ||
| 180 | |||
| 181 | .. kernel-doc:: drivers/fpga/fpga-mgr.c | ||
| 182 | :functions: fpga_mgr_unregister | ||
| 183 | |||
| 184 | API for programming a FPGA | ||
| 185 | -------------------------- | ||
| 186 | |||
| 187 | .. kernel-doc:: include/linux/fpga/fpga-mgr.h | ||
| 188 | :functions: fpga_image_info | ||
| 189 | |||
| 190 | .. kernel-doc:: include/linux/fpga/fpga-mgr.h | ||
| 191 | :functions: fpga_mgr_states | ||
| 192 | |||
| 193 | .. kernel-doc:: drivers/fpga/fpga-mgr.c | ||
| 194 | :functions: fpga_image_info_alloc | ||
| 195 | |||
| 196 | .. kernel-doc:: drivers/fpga/fpga-mgr.c | ||
| 197 | :functions: fpga_image_info_free | ||
| 198 | |||
| 199 | .. kernel-doc:: drivers/fpga/fpga-mgr.c | ||
| 200 | :functions: of_fpga_mgr_get | ||
| 201 | |||
| 202 | .. kernel-doc:: drivers/fpga/fpga-mgr.c | ||
| 203 | :functions: fpga_mgr_get | ||
| 204 | |||
| 205 | .. kernel-doc:: drivers/fpga/fpga-mgr.c | ||
| 206 | :functions: fpga_mgr_put | ||
| 207 | |||
| 208 | .. kernel-doc:: drivers/fpga/fpga-mgr.c | ||
| 209 | :functions: fpga_mgr_lock | ||
| 210 | |||
| 211 | .. kernel-doc:: drivers/fpga/fpga-mgr.c | ||
| 212 | :functions: fpga_mgr_unlock | ||
| 213 | |||
| 214 | .. kernel-doc:: include/linux/fpga/fpga-mgr.h | ||
| 215 | :functions: fpga_mgr_states | ||
| 216 | |||
| 217 | Note - use :c:func:`fpga_region_program_fpga()` instead of :c:func:`fpga_mgr_load()` | ||
| 218 | |||
| 219 | .. kernel-doc:: drivers/fpga/fpga-mgr.c | ||
| 220 | :functions: fpga_mgr_load | ||
diff --git a/Documentation/driver-api/fpga/fpga-region.rst b/Documentation/driver-api/fpga/fpga-region.rst new file mode 100644 index 000000000000..f89e4a311722 --- /dev/null +++ b/Documentation/driver-api/fpga/fpga-region.rst | |||
| @@ -0,0 +1,102 @@ | |||
| 1 | FPGA Region | ||
| 2 | =========== | ||
| 3 | |||
| 4 | Overview | ||
| 5 | -------- | ||
| 6 | |||
| 7 | This document is meant to be an brief overview of the FPGA region API usage. A | ||
| 8 | more conceptual look at regions can be found in the Device Tree binding | ||
| 9 | document [#f1]_. | ||
| 10 | |||
| 11 | For the purposes of this API document, let's just say that a region associates | ||
| 12 | an FPGA Manager and a bridge (or bridges) with a reprogrammable region of an | ||
| 13 | FPGA or the whole FPGA. The API provides a way to register a region and to | ||
| 14 | program a region. | ||
| 15 | |||
| 16 | Currently the only layer above fpga-region.c in the kernel is the Device Tree | ||
| 17 | support (of-fpga-region.c) described in [#f1]_. The DT support layer uses regions | ||
| 18 | to program the FPGA and then DT to handle enumeration. The common region code | ||
| 19 | is intended to be used by other schemes that have other ways of accomplishing | ||
| 20 | enumeration after programming. | ||
| 21 | |||
| 22 | An fpga-region can be set up to know the following things: | ||
| 23 | |||
| 24 | * which FPGA manager to use to do the programming | ||
| 25 | |||
| 26 | * which bridges to disable before programming and enable afterwards. | ||
| 27 | |||
| 28 | Additional info needed to program the FPGA image is passed in the struct | ||
| 29 | fpga_image_info including: | ||
| 30 | |||
| 31 | * pointers to the image as either a scatter-gather buffer, a contiguous | ||
| 32 | buffer, or the name of firmware file | ||
| 33 | |||
| 34 | * flags indicating specifics such as whether the image if for partial | ||
| 35 | reconfiguration. | ||
| 36 | |||
| 37 | How to program a FPGA using a region | ||
| 38 | ------------------------------------ | ||
| 39 | |||
| 40 | First, allocate the info struct:: | ||
| 41 | |||
| 42 | info = fpga_image_info_alloc(dev); | ||
| 43 | if (!info) | ||
| 44 | return -ENOMEM; | ||
| 45 | |||
| 46 | Set flags as needed, i.e.:: | ||
| 47 | |||
| 48 | info->flags |= FPGA_MGR_PARTIAL_RECONFIG; | ||
| 49 | |||
| 50 | Point to your FPGA image, such as:: | ||
| 51 | |||
| 52 | info->sgt = &sgt; | ||
| 53 | |||
| 54 | Add info to region and do the programming:: | ||
| 55 | |||
| 56 | region->info = info; | ||
| 57 | ret = fpga_region_program_fpga(region); | ||
| 58 | |||
| 59 | :c:func:`fpga_region_program_fpga()` operates on info passed in the | ||
| 60 | fpga_image_info (region->info). This function will attempt to: | ||
| 61 | |||
| 62 | * lock the region's mutex | ||
| 63 | * lock the region's FPGA manager | ||
| 64 | * build a list of FPGA bridges if a method has been specified to do so | ||
| 65 | * disable the bridges | ||
| 66 | * program the FPGA | ||
| 67 | * re-enable the bridges | ||
| 68 | * release the locks | ||
| 69 | |||
| 70 | Then you will want to enumerate whatever hardware has appeared in the FPGA. | ||
| 71 | |||
| 72 | How to add a new FPGA region | ||
| 73 | ---------------------------- | ||
| 74 | |||
| 75 | An example of usage can be seen in the probe function of [#f2]_. | ||
| 76 | |||
| 77 | .. [#f1] ../devicetree/bindings/fpga/fpga-region.txt | ||
| 78 | .. [#f2] ../../drivers/fpga/of-fpga-region.c | ||
| 79 | |||
| 80 | API to program a FGPA | ||
| 81 | --------------------- | ||
| 82 | |||
| 83 | .. kernel-doc:: drivers/fpga/fpga-region.c | ||
| 84 | :functions: fpga_region_program_fpga | ||
| 85 | |||
| 86 | API to add a new FPGA region | ||
| 87 | ---------------------------- | ||
| 88 | |||
| 89 | .. kernel-doc:: include/linux/fpga/fpga-region.h | ||
| 90 | :functions: fpga_region | ||
| 91 | |||
| 92 | .. kernel-doc:: drivers/fpga/fpga-region.c | ||
| 93 | :functions: fpga_region_create | ||
| 94 | |||
| 95 | .. kernel-doc:: drivers/fpga/fpga-region.c | ||
| 96 | :functions: fpga_region_free | ||
| 97 | |||
| 98 | .. kernel-doc:: drivers/fpga/fpga-region.c | ||
| 99 | :functions: fpga_region_register | ||
| 100 | |||
| 101 | .. kernel-doc:: drivers/fpga/fpga-region.c | ||
| 102 | :functions: fpga_region_unregister | ||
diff --git a/Documentation/driver-api/fpga/index.rst b/Documentation/driver-api/fpga/index.rst new file mode 100644 index 000000000000..c51e5ebd544a --- /dev/null +++ b/Documentation/driver-api/fpga/index.rst | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | ============== | ||
| 2 | FPGA Subsystem | ||
| 3 | ============== | ||
| 4 | |||
| 5 | :Author: Alan Tull | ||
| 6 | |||
| 7 | .. toctree:: | ||
| 8 | :maxdepth: 2 | ||
| 9 | |||
| 10 | intro | ||
| 11 | fpga-mgr | ||
| 12 | fpga-bridge | ||
| 13 | fpga-region | ||
diff --git a/Documentation/driver-api/fpga/intro.rst b/Documentation/driver-api/fpga/intro.rst new file mode 100644 index 000000000000..51cd81dbb4dc --- /dev/null +++ b/Documentation/driver-api/fpga/intro.rst | |||
| @@ -0,0 +1,54 @@ | |||
| 1 | Introduction | ||
| 2 | ============ | ||
| 3 | |||
| 4 | The FPGA subsystem supports reprogramming FPGAs dynamically under | ||
| 5 | Linux. Some of the core intentions of the FPGA subsystems are: | ||
| 6 | |||
| 7 | * The FPGA subsystem is vendor agnostic. | ||
| 8 | |||
| 9 | * The FPGA subsystem separates upper layers (userspace interfaces and | ||
| 10 | enumeration) from lower layers that know how to program a specific | ||
| 11 | FPGA. | ||
| 12 | |||
| 13 | * Code should not be shared between upper and lower layers. This | ||
| 14 | should go without saying. If that seems necessary, there's probably | ||
| 15 | framework functionality that that can be added that will benefit | ||
| 16 | other users. Write the linux-fpga mailing list and maintainers and | ||
| 17 | seek out a solution that expands the framework for broad reuse. | ||
| 18 | |||
| 19 | * Generally, when adding code, think of the future. Plan for re-use. | ||
| 20 | |||
| 21 | The framework in the kernel is divided into: | ||
| 22 | |||
| 23 | FPGA Manager | ||
| 24 | ------------ | ||
| 25 | |||
| 26 | If you are adding a new FPGA or a new method of programming a FPGA, | ||
| 27 | this is the subsystem for you. Low level FPGA manager drivers contain | ||
| 28 | the knowledge of how to program a specific device. This subsystem | ||
| 29 | includes the framework in fpga-mgr.c and the low level drivers that | ||
| 30 | are registered with it. | ||
| 31 | |||
| 32 | FPGA Bridge | ||
| 33 | ----------- | ||
| 34 | |||
| 35 | FPGA Bridges prevent spurious signals from going out of a FPGA or a | ||
| 36 | region of a FPGA during programming. They are disabled before | ||
| 37 | programming begins and re-enabled afterwards. An FPGA bridge may be | ||
| 38 | actual hard hardware that gates a bus to a cpu or a soft ("freeze") | ||
| 39 | bridge in FPGA fabric that surrounds a partial reconfiguration region | ||
| 40 | of an FPGA. This subsystem includes fpga-bridge.c and the low level | ||
| 41 | drivers that are registered with it. | ||
| 42 | |||
| 43 | FPGA Region | ||
| 44 | ----------- | ||
| 45 | |||
| 46 | If you are adding a new interface to the FPGA framework, add it on top | ||
| 47 | of a FPGA region to allow the most reuse of your interface. | ||
| 48 | |||
| 49 | The FPGA Region framework (fpga-region.c) associates managers and | ||
| 50 | bridges as reconfigurable regions. A region may refer to the whole | ||
| 51 | FPGA in full reconfiguration or to a partial reconfiguration region. | ||
| 52 | |||
| 53 | The Device Tree FPGA Region support (of-fpga-region.c) handles | ||
| 54 | reprogramming FPGAs when device tree overlays are applied. | ||
diff --git a/Documentation/driver-api/index.rst b/Documentation/driver-api/index.rst index 5d04296f5ce0..f4180e7c7ed5 100644 --- a/Documentation/driver-api/index.rst +++ b/Documentation/driver-api/index.rst | |||
| @@ -51,6 +51,7 @@ available subsections can be seen below. | |||
| 51 | dmaengine/index | 51 | dmaengine/index |
| 52 | slimbus | 52 | slimbus |
| 53 | soundwire/index | 53 | soundwire/index |
| 54 | fpga/index | ||
| 54 | 55 | ||
| 55 | .. only:: subproject and html | 56 | .. only:: subproject and html |
| 56 | 57 | ||
diff --git a/Documentation/driver-api/soundwire/error_handling.rst b/Documentation/driver-api/soundwire/error_handling.rst new file mode 100644 index 000000000000..aa3a0a23a066 --- /dev/null +++ b/Documentation/driver-api/soundwire/error_handling.rst | |||
| @@ -0,0 +1,65 @@ | |||
| 1 | ======================== | ||
| 2 | SoundWire Error Handling | ||
| 3 | ======================== | ||
| 4 | |||
| 5 | The SoundWire PHY was designed with care and errors on the bus are going to | ||
| 6 | be very unlikely, and if they happen it should be limited to single bit | ||
| 7 | errors. Examples of this design can be found in the synchronization | ||
| 8 | mechanism (sync loss after two errors) and short CRCs used for the Bulk | ||
| 9 | Register Access. | ||
| 10 | |||
| 11 | The errors can be detected with multiple mechanisms: | ||
| 12 | |||
| 13 | 1. Bus clash or parity errors: This mechanism relies on low-level detectors | ||
| 14 | that are independent of the payload and usages, and they cover both control | ||
| 15 | and audio data. The current implementation only logs such errors. | ||
| 16 | Improvements could be invalidating an entire programming sequence and | ||
| 17 | restarting from a known position. In the case of such errors outside of a | ||
| 18 | control/command sequence, there is no concealment or recovery for audio | ||
| 19 | data enabled by the SoundWire protocol, the location of the error will also | ||
| 20 | impact its audibility (most-significant bits will be more impacted in PCM), | ||
| 21 | and after a number of such errors are detected the bus might be reset. Note | ||
| 22 | that bus clashes due to programming errors (two streams using the same bit | ||
| 23 | slots) or electrical issues during the transmit/receive transition cannot | ||
| 24 | be distinguished, although a recurring bus clash when audio is enabled is a | ||
| 25 | indication of a bus allocation issue. The interrupt mechanism can also help | ||
| 26 | identify Slaves which detected a Bus Clash or a Parity Error, but they may | ||
| 27 | not be responsible for the errors so resetting them individually is not a | ||
| 28 | viable recovery strategy. | ||
| 29 | |||
| 30 | 2. Command status: Each command is associated with a status, which only | ||
| 31 | covers transmission of the data between devices. The ACK status indicates | ||
| 32 | that the command was received and will be executed by the end of the | ||
| 33 | current frame. A NAK indicates that the command was in error and will not | ||
| 34 | be applied. In case of a bad programming (command sent to non-existent | ||
| 35 | Slave or to a non-implemented register) or electrical issue, no response | ||
| 36 | signals the command was ignored. Some Master implementations allow for a | ||
| 37 | command to be retransmitted several times. If the retransmission fails, | ||
| 38 | backtracking and restarting the entire programming sequence might be a | ||
| 39 | solution. Alternatively some implementations might directly issue a bus | ||
| 40 | reset and re-enumerate all devices. | ||
| 41 | |||
| 42 | 3. Timeouts: In a number of cases such as ChannelPrepare or | ||
| 43 | ClockStopPrepare, the bus driver is supposed to poll a register field until | ||
| 44 | it transitions to a NotFinished value of zero. The MIPI SoundWire spec 1.1 | ||
| 45 | does not define timeouts but the MIPI SoundWire DisCo document adds | ||
| 46 | recommendation on timeouts. If such configurations do not complete, the | ||
| 47 | driver will return a -ETIMEOUT. Such timeouts are symptoms of a faulty | ||
| 48 | Slave device and are likely impossible to recover from. | ||
| 49 | |||
| 50 | Errors during global reconfiguration sequences are extremely difficult to | ||
| 51 | handle: | ||
| 52 | |||
| 53 | 1. BankSwitch: An error during the last command issuing a BankSwitch is | ||
| 54 | difficult to backtrack from. Retransmitting the Bank Switch command may be | ||
| 55 | possible in a single segment setup, but this can lead to synchronization | ||
| 56 | problems when enabling multiple bus segments (a command with side effects | ||
| 57 | such as frame reconfiguration would be handled at different times). A global | ||
| 58 | hard-reset might be the best solution. | ||
| 59 | |||
| 60 | Note that SoundWire does not provide a mechanism to detect illegal values | ||
| 61 | written in valid registers. In a number of cases the standard even mentions | ||
| 62 | that the Slave might behave in implementation-defined ways. The bus | ||
| 63 | implementation does not provide a recovery mechanism for such errors, Slave | ||
| 64 | or Master driver implementers are responsible for writing valid values in | ||
| 65 | valid registers and implement additional range checking if needed. | ||
diff --git a/Documentation/driver-api/soundwire/index.rst b/Documentation/driver-api/soundwire/index.rst index 647e94654752..6db026028f27 100644 --- a/Documentation/driver-api/soundwire/index.rst +++ b/Documentation/driver-api/soundwire/index.rst | |||
| @@ -6,6 +6,9 @@ SoundWire Documentation | |||
| 6 | :maxdepth: 1 | 6 | :maxdepth: 1 |
| 7 | 7 | ||
| 8 | summary | 8 | summary |
| 9 | stream | ||
| 10 | error_handling | ||
| 11 | locking | ||
| 9 | 12 | ||
| 10 | .. only:: subproject | 13 | .. only:: subproject |
| 11 | 14 | ||
diff --git a/Documentation/driver-api/soundwire/locking.rst b/Documentation/driver-api/soundwire/locking.rst new file mode 100644 index 000000000000..253f73555255 --- /dev/null +++ b/Documentation/driver-api/soundwire/locking.rst | |||
| @@ -0,0 +1,106 @@ | |||
| 1 | ================= | ||
| 2 | SoundWire Locking | ||
| 3 | ================= | ||
| 4 | |||
| 5 | This document explains locking mechanism of the SoundWire Bus. Bus uses | ||
| 6 | following locks in order to avoid race conditions in Bus operations on | ||
| 7 | shared resources. | ||
| 8 | |||
| 9 | - Bus lock | ||
| 10 | |||
| 11 | - Message lock | ||
| 12 | |||
| 13 | Bus lock | ||
| 14 | ======== | ||
| 15 | |||
| 16 | SoundWire Bus lock is a mutex and is part of Bus data structure | ||
| 17 | (sdw_bus) which is used for every Bus instance. This lock is used to | ||
| 18 | serialize each of the following operations(s) within SoundWire Bus instance. | ||
| 19 | |||
| 20 | - Addition and removal of Slave(s), changing Slave status. | ||
| 21 | |||
| 22 | - Prepare, Enable, Disable and De-prepare stream operations. | ||
| 23 | |||
| 24 | - Access of Stream data structure. | ||
| 25 | |||
| 26 | Message lock | ||
| 27 | ============ | ||
| 28 | |||
| 29 | SoundWire message transfer lock. This mutex is part of | ||
| 30 | Bus data structure (sdw_bus). This lock is used to serialize the message | ||
| 31 | transfers (read/write) within a SoundWire Bus instance. | ||
| 32 | |||
| 33 | Below examples show how locks are acquired. | ||
| 34 | |||
| 35 | Example 1 | ||
| 36 | --------- | ||
| 37 | |||
| 38 | Message transfer. | ||
| 39 | |||
| 40 | 1. For every message transfer | ||
| 41 | |||
| 42 | a. Acquire Message lock. | ||
| 43 | |||
| 44 | b. Transfer message (Read/Write) to Slave1 or broadcast message on | ||
| 45 | Bus in case of bank switch. | ||
| 46 | |||
| 47 | c. Release Message lock :: | ||
| 48 | |||
| 49 | +----------+ +---------+ | ||
| 50 | | | | | | ||
| 51 | | Bus | | Master | | ||
| 52 | | | | Driver | | ||
| 53 | | | | | | ||
| 54 | +----+-----+ +----+----+ | ||
| 55 | | | | ||
| 56 | | bus->ops->xfer_msg() | | ||
| 57 | <-------------------------------+ a. Acquire Message lock | ||
| 58 | | | b. Transfer message | ||
| 59 | | | | ||
| 60 | +-------------------------------> c. Release Message lock | ||
| 61 | | return success/error | d. Return success/error | ||
| 62 | | | | ||
| 63 | + + | ||
| 64 | |||
| 65 | Example 2 | ||
| 66 | --------- | ||
| 67 | |||
| 68 | Prepare operation. | ||
| 69 | |||
| 70 | 1. Acquire lock for Bus instance associated with Master 1. | ||
| 71 | |||
| 72 | 2. For every message transfer in Prepare operation | ||
| 73 | |||
| 74 | a. Acquire Message lock. | ||
| 75 | |||
| 76 | b. Transfer message (Read/Write) to Slave1 or broadcast message on | ||
| 77 | Bus in case of bank switch. | ||
| 78 | |||
| 79 | c. Release Message lock. | ||
| 80 | |||
| 81 | 3. Release lock for Bus instance associated with Master 1 :: | ||
| 82 | |||
| 83 | +----------+ +---------+ | ||
| 84 | | | | | | ||
| 85 | | Bus | | Master | | ||
| 86 | | | | Driver | | ||
| 87 | | | | | | ||
| 88 | +----+-----+ +----+----+ | ||
| 89 | | | | ||
| 90 | | sdw_prepare_stream() | | ||
| 91 | <-------------------------------+ 1. Acquire bus lock | ||
| 92 | | | 2. Perform stream prepare | ||
| 93 | | | | ||
| 94 | | | | ||
| 95 | | bus->ops->xfer_msg() | | ||
| 96 | <-------------------------------+ a. Acquire Message lock | ||
| 97 | | | b. Transfer message | ||
| 98 | | | | ||
| 99 | +-------------------------------> c. Release Message lock | ||
| 100 | | return success/error | d. Return success/error | ||
| 101 | | | | ||
| 102 | | | | ||
| 103 | | return success/error | 3. Release bus lock | ||
| 104 | +-------------------------------> 4. Return success/error | ||
| 105 | | | | ||
| 106 | + + | ||
diff --git a/Documentation/driver-api/soundwire/stream.rst b/Documentation/driver-api/soundwire/stream.rst new file mode 100644 index 000000000000..29121aa55fb9 --- /dev/null +++ b/Documentation/driver-api/soundwire/stream.rst | |||
| @@ -0,0 +1,372 @@ | |||
| 1 | ========================= | ||
| 2 | Audio Stream in SoundWire | ||
| 3 | ========================= | ||
| 4 | |||
| 5 | An audio stream is a logical or virtual connection created between | ||
| 6 | |||
| 7 | (1) System memory buffer(s) and Codec(s) | ||
| 8 | |||
| 9 | (2) DSP memory buffer(s) and Codec(s) | ||
| 10 | |||
| 11 | (3) FIFO(s) and Codec(s) | ||
| 12 | |||
| 13 | (4) Codec(s) and Codec(s) | ||
| 14 | |||
| 15 | which is typically driven by a DMA(s) channel through the data link. An | ||
| 16 | audio stream contains one or more channels of data. All channels within | ||
| 17 | stream must have same sample rate and same sample size. | ||
| 18 | |||
| 19 | Assume a stream with two channels (Left & Right) is opened using SoundWire | ||
| 20 | interface. Below are some ways a stream can be represented in SoundWire. | ||
| 21 | |||
| 22 | Stream Sample in memory (System memory, DSP memory or FIFOs) :: | ||
| 23 | |||
| 24 | ------------------------- | ||
| 25 | | L | R | L | R | L | R | | ||
| 26 | ------------------------- | ||
| 27 | |||
| 28 | Example 1: Stereo Stream with L and R channels is rendered from Master to | ||
| 29 | Slave. Both Master and Slave is using single port. :: | ||
| 30 | |||
| 31 | +---------------+ Clock Signal +---------------+ | ||
| 32 | | Master +----------------------------------+ Slave | | ||
| 33 | | Interface | | Interface | | ||
| 34 | | | | 1 | | ||
| 35 | | | Data Signal | | | ||
| 36 | | L + R +----------------------------------+ L + R | | ||
| 37 | | (Data) | Data Direction | (Data) | | ||
| 38 | +---------------+ +-----------------------> +---------------+ | ||
| 39 | |||
| 40 | |||
| 41 | Example 2: Stereo Stream with L and R channels is captured from Slave to | ||
| 42 | Master. Both Master and Slave is using single port. :: | ||
| 43 | |||
| 44 | |||
| 45 | +---------------+ Clock Signal +---------------+ | ||
| 46 | | Master +----------------------------------+ Slave | | ||
| 47 | | Interface | | Interface | | ||
| 48 | | | | 1 | | ||
| 49 | | | Data Signal | | | ||
| 50 | | L + R +----------------------------------+ L + R | | ||
| 51 | | (Data) | Data Direction | (Data) | | ||
| 52 | +---------------+ <-----------------------+ +---------------+ | ||
| 53 | |||
| 54 | |||
| 55 | Example 3: Stereo Stream with L and R channels is rendered by Master. Each | ||
| 56 | of the L and R channel is received by two different Slaves. Master and both | ||
| 57 | Slaves are using single port. :: | ||
| 58 | |||
| 59 | +---------------+ Clock Signal +---------------+ | ||
| 60 | | Master +---------+------------------------+ Slave | | ||
| 61 | | Interface | | | Interface | | ||
| 62 | | | | | 1 | | ||
| 63 | | | | Data Signal | | | ||
| 64 | | L + R +---+------------------------------+ L | | ||
| 65 | | (Data) | | | Data Direction | (Data) | | ||
| 66 | +---------------+ | | +-------------> +---------------+ | ||
| 67 | | | | ||
| 68 | | | | ||
| 69 | | | +---------------+ | ||
| 70 | | +----------------------> | Slave | | ||
| 71 | | | Interface | | ||
| 72 | | | 2 | | ||
| 73 | | | | | ||
| 74 | +----------------------------> | R | | ||
| 75 | | (Data) | | ||
| 76 | +---------------+ | ||
| 77 | |||
| 78 | |||
| 79 | Example 4: Stereo Stream with L and R channel is rendered by two different | ||
| 80 | Ports of the Master and is received by only single Port of the Slave | ||
| 81 | interface. :: | ||
| 82 | |||
| 83 | +--------------------+ | ||
| 84 | | | | ||
| 85 | | +--------------+ +----------------+ | ||
| 86 | | | || | | | ||
| 87 | | | Data Port || L Channel | | | ||
| 88 | | | 1 |------------+ | | | ||
| 89 | | | L Channel || | +-----+----+ | | ||
| 90 | | | (Data) || | L + R Channel || Data | | | ||
| 91 | | Master +----------+ | +---+---------> || Port | | | ||
| 92 | | Interface | | || 1 | | | ||
| 93 | | +--------------+ | || | | | ||
| 94 | | | || | +----------+ | | ||
| 95 | | | Data Port |------------+ | | | ||
| 96 | | | 2 || R Channel | Slave | | ||
| 97 | | | R Channel || | Interface | | ||
| 98 | | | (Data) || | 1 | | ||
| 99 | | +--------------+ Clock Signal | L + R | | ||
| 100 | | +---------------------------> | (Data) | | ||
| 101 | +--------------------+ | | | ||
| 102 | +----------------+ | ||
| 103 | |||
| 104 | SoundWire Stream Management flow | ||
| 105 | ================================ | ||
| 106 | |||
| 107 | Stream definitions | ||
| 108 | ------------------ | ||
| 109 | |||
| 110 | (1) Current stream: This is classified as the stream on which operation has | ||
| 111 | to be performed like prepare, enable, disable, de-prepare etc. | ||
| 112 | |||
| 113 | (2) Active stream: This is classified as the stream which is already active | ||
| 114 | on Bus other than current stream. There can be multiple active streams | ||
| 115 | on the Bus. | ||
| 116 | |||
| 117 | SoundWire Bus manages stream operations for each stream getting | ||
| 118 | rendered/captured on the SoundWire Bus. This section explains Bus operations | ||
| 119 | done for each of the stream allocated/released on Bus. Following are the | ||
| 120 | stream states maintained by the Bus for each of the audio stream. | ||
| 121 | |||
| 122 | |||
| 123 | SoundWire stream states | ||
| 124 | ----------------------- | ||
| 125 | |||
| 126 | Below shows the SoundWire stream states and state transition diagram. :: | ||
| 127 | |||
| 128 | +-----------+ +------------+ +----------+ +----------+ | ||
| 129 | | ALLOCATED +---->| CONFIGURED +---->| PREPARED +---->| ENABLED | | ||
| 130 | | STATE | | STATE | | STATE | | STATE | | ||
| 131 | +-----------+ +------------+ +----------+ +----+-----+ | ||
| 132 | ^ | ||
| 133 | | | ||
| 134 | | | ||
| 135 | v | ||
| 136 | +----------+ +------------+ +----+-----+ | ||
| 137 | | RELEASED |<----------+ DEPREPARED |<-------+ DISABLED | | ||
| 138 | | STATE | | STATE | | STATE | | ||
| 139 | +----------+ +------------+ +----------+ | ||
| 140 | |||
| 141 | NOTE: State transition between prepare and deprepare is supported in Spec | ||
| 142 | but not in the software (subsystem) | ||
| 143 | |||
| 144 | NOTE2: Stream state transition checks need to be handled by caller | ||
| 145 | framework, for example ALSA/ASoC. No checks for stream transition exist in | ||
| 146 | SoundWire subsystem. | ||
| 147 | |||
| 148 | Stream State Operations | ||
| 149 | ----------------------- | ||
| 150 | |||
| 151 | Below section explains the operations done by the Bus on Master(s) and | ||
| 152 | Slave(s) as part of stream state transitions. | ||
| 153 | |||
| 154 | SDW_STREAM_ALLOCATED | ||
| 155 | ~~~~~~~~~~~~~~~~~~~~ | ||
| 156 | |||
| 157 | Allocation state for stream. This is the entry state | ||
| 158 | of the stream. Operations performed before entering in this state: | ||
| 159 | |||
| 160 | (1) A stream runtime is allocated for the stream. This stream | ||
| 161 | runtime is used as a reference for all the operations performed | ||
| 162 | on the stream. | ||
| 163 | |||
| 164 | (2) The resources required for holding stream runtime information are | ||
| 165 | allocated and initialized. This holds all stream related information | ||
| 166 | such as stream type (PCM/PDM) and parameters, Master and Slave | ||
| 167 | interface associated with the stream, stream state etc. | ||
| 168 | |||
| 169 | After all above operations are successful, stream state is set to | ||
| 170 | ``SDW_STREAM_ALLOCATED``. | ||
| 171 | |||
| 172 | Bus implements below API for allocate a stream which needs to be called once | ||
| 173 | per stream. From ASoC DPCM framework, this stream state maybe linked to | ||
| 174 | .startup() operation. | ||
| 175 | |||
| 176 | .. code-block:: c | ||
| 177 | int sdw_alloc_stream(char * stream_name); | ||
| 178 | |||
| 179 | |||
| 180 | SDW_STREAM_CONFIGURED | ||
| 181 | ~~~~~~~~~~~~~~~~~~~~~ | ||
| 182 | |||
| 183 | Configuration state of stream. Operations performed before entering in | ||
| 184 | this state: | ||
| 185 | |||
| 186 | (1) The resources allocated for stream information in SDW_STREAM_ALLOCATED | ||
| 187 | state are updated here. This includes stream parameters, Master(s) | ||
| 188 | and Slave(s) runtime information associated with current stream. | ||
| 189 | |||
| 190 | (2) All the Master(s) and Slave(s) associated with current stream provide | ||
| 191 | the port information to Bus which includes port numbers allocated by | ||
| 192 | Master(s) and Slave(s) for current stream and their channel mask. | ||
| 193 | |||
| 194 | After all above operations are successful, stream state is set to | ||
| 195 | ``SDW_STREAM_CONFIGURED``. | ||
| 196 | |||
| 197 | Bus implements below APIs for CONFIG state which needs to be called by | ||
| 198 | the respective Master(s) and Slave(s) associated with stream. These APIs can | ||
| 199 | only be invoked once by respective Master(s) and Slave(s). From ASoC DPCM | ||
| 200 | framework, this stream state is linked to .hw_params() operation. | ||
| 201 | |||
| 202 | .. code-block:: c | ||
| 203 | int sdw_stream_add_master(struct sdw_bus * bus, | ||
| 204 | struct sdw_stream_config * stream_config, | ||
| 205 | struct sdw_ports_config * ports_config, | ||
| 206 | struct sdw_stream_runtime * stream); | ||
| 207 | |||
| 208 | int sdw_stream_add_slave(struct sdw_slave * slave, | ||
| 209 | struct sdw_stream_config * stream_config, | ||
| 210 | struct sdw_ports_config * ports_config, | ||
| 211 | struct sdw_stream_runtime * stream); | ||
| 212 | |||
| 213 | |||
| 214 | SDW_STREAM_PREPARED | ||
| 215 | ~~~~~~~~~~~~~~~~~~~ | ||
| 216 | |||
| 217 | Prepare state of stream. Operations performed before entering in this state: | ||
| 218 | |||
| 219 | (1) Bus parameters such as bandwidth, frame shape, clock frequency, | ||
| 220 | are computed based on current stream as well as already active | ||
| 221 | stream(s) on Bus. Re-computation is required to accommodate current | ||
| 222 | stream on the Bus. | ||
| 223 | |||
| 224 | (2) Transport and port parameters of all Master(s) and Slave(s) port(s) are | ||
| 225 | computed for the current as well as already active stream based on frame | ||
| 226 | shape and clock frequency computed in step 1. | ||
| 227 | |||
| 228 | (3) Computed Bus and transport parameters are programmed in Master(s) and | ||
| 229 | Slave(s) registers. The banked registers programming is done on the | ||
| 230 | alternate bank (bank currently unused). Port(s) are enabled for the | ||
| 231 | already active stream(s) on the alternate bank (bank currently unused). | ||
| 232 | This is done in order to not disrupt already active stream(s). | ||
| 233 | |||
| 234 | (4) Once all the values are programmed, Bus initiates switch to alternate | ||
| 235 | bank where all new values programmed gets into effect. | ||
| 236 | |||
| 237 | (5) Ports of Master(s) and Slave(s) for current stream are prepared by | ||
| 238 | programming PrepareCtrl register. | ||
| 239 | |||
| 240 | After all above operations are successful, stream state is set to | ||
| 241 | ``SDW_STREAM_PREPARED``. | ||
| 242 | |||
| 243 | Bus implements below API for PREPARE state which needs to be called once per | ||
| 244 | stream. From ASoC DPCM framework, this stream state is linked to | ||
| 245 | .prepare() operation. | ||
| 246 | |||
| 247 | .. code-block:: c | ||
| 248 | int sdw_prepare_stream(struct sdw_stream_runtime * stream); | ||
| 249 | |||
| 250 | |||
| 251 | SDW_STREAM_ENABLED | ||
| 252 | ~~~~~~~~~~~~~~~~~~ | ||
| 253 | |||
| 254 | Enable state of stream. The data port(s) are enabled upon entering this state. | ||
| 255 | Operations performed before entering in this state: | ||
| 256 | |||
| 257 | (1) All the values computed in SDW_STREAM_PREPARED state are programmed | ||
| 258 | in alternate bank (bank currently unused). It includes programming of | ||
| 259 | already active stream(s) as well. | ||
| 260 | |||
| 261 | (2) All the Master(s) and Slave(s) port(s) for the current stream are | ||
| 262 | enabled on alternate bank (bank currently unused) by programming | ||
| 263 | ChannelEn register. | ||
| 264 | |||
| 265 | (3) Once all the values are programmed, Bus initiates switch to alternate | ||
| 266 | bank where all new values programmed gets into effect and port(s) | ||
| 267 | associated with current stream are enabled. | ||
| 268 | |||
| 269 | After all above operations are successful, stream state is set to | ||
| 270 | ``SDW_STREAM_ENABLED``. | ||
| 271 | |||
| 272 | Bus implements below API for ENABLE state which needs to be called once per | ||
| 273 | stream. From ASoC DPCM framework, this stream state is linked to | ||
| 274 | .trigger() start operation. | ||
| 275 | |||
| 276 | .. code-block:: c | ||
| 277 | int sdw_enable_stream(struct sdw_stream_runtime * stream); | ||
| 278 | |||
| 279 | SDW_STREAM_DISABLED | ||
| 280 | ~~~~~~~~~~~~~~~~~~~ | ||
| 281 | |||
| 282 | Disable state of stream. The data port(s) are disabled upon exiting this state. | ||
| 283 | Operations performed before entering in this state: | ||
| 284 | |||
| 285 | (1) All the Master(s) and Slave(s) port(s) for the current stream are | ||
| 286 | disabled on alternate bank (bank currently unused) by programming | ||
| 287 | ChannelEn register. | ||
| 288 | |||
| 289 | (2) All the current configuration of Bus and active stream(s) are programmed | ||
| 290 | into alternate bank (bank currently unused). | ||
| 291 | |||
| 292 | (3) Once all the values are programmed, Bus initiates switch to alternate | ||
| 293 | bank where all new values programmed gets into effect and port(s) associated | ||
| 294 | with current stream are disabled. | ||
| 295 | |||
| 296 | After all above operations are successful, stream state is set to | ||
| 297 | ``SDW_STREAM_DISABLED``. | ||
| 298 | |||
| 299 | Bus implements below API for DISABLED state which needs to be called once | ||
| 300 | per stream. From ASoC DPCM framework, this stream state is linked to | ||
| 301 | .trigger() stop operation. | ||
| 302 | |||
| 303 | .. code-block:: c | ||
| 304 | int sdw_disable_stream(struct sdw_stream_runtime * stream); | ||
| 305 | |||
| 306 | |||
| 307 | SDW_STREAM_DEPREPARED | ||
| 308 | ~~~~~~~~~~~~~~~~~~~~~ | ||
| 309 | |||
| 310 | De-prepare state of stream. Operations performed before entering in this | ||
| 311 | state: | ||
| 312 | |||
| 313 | (1) All the port(s) of Master(s) and Slave(s) for current stream are | ||
| 314 | de-prepared by programming PrepareCtrl register. | ||
| 315 | |||
| 316 | (2) The payload bandwidth of current stream is reduced from the total | ||
| 317 | bandwidth requirement of bus and new parameters calculated and | ||
| 318 | applied by performing bank switch etc. | ||
| 319 | |||
| 320 | After all above operations are successful, stream state is set to | ||
| 321 | ``SDW_STREAM_DEPREPARED``. | ||
| 322 | |||
| 323 | Bus implements below API for DEPREPARED state which needs to be called once | ||
| 324 | per stream. From ASoC DPCM framework, this stream state is linked to | ||
| 325 | .trigger() stop operation. | ||
| 326 | |||
| 327 | .. code-block:: c | ||
| 328 | int sdw_deprepare_stream(struct sdw_stream_runtime * stream); | ||
| 329 | |||
| 330 | |||
| 331 | SDW_STREAM_RELEASED | ||
| 332 | ~~~~~~~~~~~~~~~~~~~ | ||
| 333 | |||
| 334 | Release state of stream. Operations performed before entering in this state: | ||
| 335 | |||
| 336 | (1) Release port resources for all Master(s) and Slave(s) port(s) | ||
| 337 | associated with current stream. | ||
| 338 | |||
| 339 | (2) Release Master(s) and Slave(s) runtime resources associated with | ||
| 340 | current stream. | ||
| 341 | |||
| 342 | (3) Release stream runtime resources associated with current stream. | ||
| 343 | |||
| 344 | After all above operations are successful, stream state is set to | ||
| 345 | ``SDW_STREAM_RELEASED``. | ||
| 346 | |||
| 347 | Bus implements below APIs for RELEASE state which needs to be called by | ||
| 348 | all the Master(s) and Slave(s) associated with stream. From ASoC DPCM | ||
| 349 | framework, this stream state is linked to .hw_free() operation. | ||
| 350 | |||
| 351 | .. code-block:: c | ||
| 352 | int sdw_stream_remove_master(struct sdw_bus * bus, | ||
| 353 | struct sdw_stream_runtime * stream); | ||
| 354 | int sdw_stream_remove_slave(struct sdw_slave * slave, | ||
| 355 | struct sdw_stream_runtime * stream); | ||
| 356 | |||
| 357 | |||
| 358 | The .shutdown() ASoC DPCM operation calls below Bus API to release | ||
| 359 | stream assigned as part of ALLOCATED state. | ||
| 360 | |||
| 361 | In .shutdown() the data structure maintaining stream state are freed up. | ||
| 362 | |||
| 363 | .. code-block:: c | ||
| 364 | void sdw_release_stream(struct sdw_stream_runtime * stream); | ||
| 365 | |||
| 366 | Not Supported | ||
| 367 | ============= | ||
| 368 | |||
| 369 | 1. A single port with multiple channels supported cannot be used between two | ||
| 370 | streams or across stream. For example a port with 4 channels cannot be used | ||
| 371 | to handle 2 independent stereo streams even though it's possible in theory | ||
| 372 | in SoundWire. | ||
diff --git a/Documentation/fpga/fpga-mgr.txt b/Documentation/fpga/fpga-mgr.txt deleted file mode 100644 index cc6413ed6fc9..000000000000 --- a/Documentation/fpga/fpga-mgr.txt +++ /dev/null | |||
| @@ -1,199 +0,0 @@ | |||
| 1 | FPGA Manager Core | ||
| 2 | |||
| 3 | Alan Tull 2015 | ||
| 4 | |||
| 5 | Overview | ||
| 6 | ======== | ||
| 7 | |||
| 8 | The FPGA manager core exports a set of functions for programming an FPGA with | ||
| 9 | an image. The API is manufacturer agnostic. All manufacturer specifics are | ||
| 10 | hidden away in a low level driver which registers a set of ops with the core. | ||
| 11 | The FPGA image data itself is very manufacturer specific, but for our purposes | ||
| 12 | it's just binary data. The FPGA manager core won't parse it. | ||
| 13 | |||
| 14 | The FPGA image to be programmed can be in a scatter gather list, a single | ||
| 15 | contiguous buffer, or a firmware file. Because allocating contiguous kernel | ||
| 16 | memory for the buffer should be avoided, users are encouraged to use a scatter | ||
| 17 | gather list instead if possible. | ||
| 18 | |||
| 19 | The particulars for programming the image are presented in a structure (struct | ||
| 20 | fpga_image_info). This struct contains parameters such as pointers to the | ||
| 21 | FPGA image as well as image-specific particulars such as whether the image was | ||
| 22 | built for full or partial reconfiguration. | ||
| 23 | |||
| 24 | API Functions: | ||
| 25 | ============== | ||
| 26 | |||
| 27 | To program the FPGA: | ||
| 28 | -------------------- | ||
| 29 | |||
| 30 | int fpga_mgr_load(struct fpga_manager *mgr, | ||
| 31 | struct fpga_image_info *info); | ||
| 32 | |||
| 33 | Load the FPGA from an image which is indicated in the info. If successful, | ||
| 34 | the FPGA ends up in operating mode. Return 0 on success or a negative error | ||
| 35 | code. | ||
| 36 | |||
| 37 | To allocate or free a struct fpga_image_info: | ||
| 38 | --------------------------------------------- | ||
| 39 | |||
| 40 | struct fpga_image_info *fpga_image_info_alloc(struct device *dev); | ||
| 41 | |||
| 42 | void fpga_image_info_free(struct fpga_image_info *info); | ||
| 43 | |||
| 44 | To get/put a reference to a FPGA manager: | ||
| 45 | ----------------------------------------- | ||
| 46 | |||
| 47 | struct fpga_manager *of_fpga_mgr_get(struct device_node *node); | ||
| 48 | struct fpga_manager *fpga_mgr_get(struct device *dev); | ||
| 49 | void fpga_mgr_put(struct fpga_manager *mgr); | ||
| 50 | |||
| 51 | Given a DT node or device, get a reference to a FPGA manager. This pointer | ||
| 52 | can be saved until you are ready to program the FPGA. fpga_mgr_put releases | ||
| 53 | the reference. | ||
| 54 | |||
| 55 | |||
| 56 | To get exclusive control of a FPGA manager: | ||
| 57 | ------------------------------------------- | ||
| 58 | |||
| 59 | int fpga_mgr_lock(struct fpga_manager *mgr); | ||
| 60 | void fpga_mgr_unlock(struct fpga_manager *mgr); | ||
| 61 | |||
| 62 | The user should call fpga_mgr_lock and verify that it returns 0 before | ||
| 63 | attempting to program the FPGA. Likewise, the user should call | ||
| 64 | fpga_mgr_unlock when done programming the FPGA. | ||
| 65 | |||
| 66 | |||
| 67 | To register or unregister the low level FPGA-specific driver: | ||
| 68 | ------------------------------------------------------------- | ||
| 69 | |||
| 70 | int fpga_mgr_register(struct device *dev, const char *name, | ||
| 71 | const struct fpga_manager_ops *mops, | ||
| 72 | void *priv); | ||
| 73 | |||
| 74 | void fpga_mgr_unregister(struct device *dev); | ||
| 75 | |||
| 76 | Use of these two functions is described below in "How To Support a new FPGA | ||
| 77 | device." | ||
| 78 | |||
| 79 | |||
| 80 | How to write an image buffer to a supported FPGA | ||
| 81 | ================================================ | ||
| 82 | #include <linux/fpga/fpga-mgr.h> | ||
| 83 | |||
| 84 | struct fpga_manager *mgr; | ||
| 85 | struct fpga_image_info *info; | ||
| 86 | int ret; | ||
| 87 | |||
| 88 | /* | ||
| 89 | * Get a reference to FPGA manager. The manager is not locked, so you can | ||
| 90 | * hold onto this reference without it preventing programming. | ||
| 91 | * | ||
| 92 | * This example uses the device node of the manager. Alternatively, use | ||
| 93 | * fpga_mgr_get(dev) instead if you have the device. | ||
| 94 | */ | ||
| 95 | mgr = of_fpga_mgr_get(mgr_node); | ||
| 96 | |||
| 97 | /* struct with information about the FPGA image to program. */ | ||
| 98 | info = fpga_image_info_alloc(dev); | ||
| 99 | |||
| 100 | /* flags indicates whether to do full or partial reconfiguration */ | ||
| 101 | info->flags = FPGA_MGR_PARTIAL_RECONFIG; | ||
| 102 | |||
| 103 | /* | ||
| 104 | * At this point, indicate where the image is. This is pseudo-code; you're | ||
| 105 | * going to use one of these three. | ||
| 106 | */ | ||
| 107 | if (image is in a scatter gather table) { | ||
| 108 | |||
| 109 | info->sgt = [your scatter gather table] | ||
| 110 | |||
| 111 | } else if (image is in a buffer) { | ||
| 112 | |||
| 113 | info->buf = [your image buffer] | ||
| 114 | info->count = [image buffer size] | ||
| 115 | |||
| 116 | } else if (image is in a firmware file) { | ||
| 117 | |||
| 118 | info->firmware_name = devm_kstrdup(dev, firmware_name, GFP_KERNEL); | ||
| 119 | |||
| 120 | } | ||
| 121 | |||
| 122 | /* Get exclusive control of FPGA manager */ | ||
| 123 | ret = fpga_mgr_lock(mgr); | ||
| 124 | |||
| 125 | /* Load the buffer to the FPGA */ | ||
| 126 | ret = fpga_mgr_buf_load(mgr, &info, buf, count); | ||
| 127 | |||
| 128 | /* Release the FPGA manager */ | ||
| 129 | fpga_mgr_unlock(mgr); | ||
| 130 | fpga_mgr_put(mgr); | ||
| 131 | |||
| 132 | /* Deallocate the image info if you're done with it */ | ||
| 133 | fpga_image_info_free(info); | ||
| 134 | |||
| 135 | How to support a new FPGA device | ||
| 136 | ================================ | ||
| 137 | To add another FPGA manager, write a driver that implements a set of ops. The | ||
| 138 | probe function calls fpga_mgr_register(), such as: | ||
| 139 | |||
| 140 | static const struct fpga_manager_ops socfpga_fpga_ops = { | ||
| 141 | .write_init = socfpga_fpga_ops_configure_init, | ||
| 142 | .write = socfpga_fpga_ops_configure_write, | ||
| 143 | .write_complete = socfpga_fpga_ops_configure_complete, | ||
| 144 | .state = socfpga_fpga_ops_state, | ||
| 145 | }; | ||
| 146 | |||
| 147 | static int socfpga_fpga_probe(struct platform_device *pdev) | ||
| 148 | { | ||
| 149 | struct device *dev = &pdev->dev; | ||
| 150 | struct socfpga_fpga_priv *priv; | ||
| 151 | int ret; | ||
| 152 | |||
| 153 | priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); | ||
| 154 | if (!priv) | ||
| 155 | return -ENOMEM; | ||
| 156 | |||
| 157 | /* ... do ioremaps, get interrupts, etc. and save | ||
| 158 | them in priv... */ | ||
| 159 | |||
| 160 | return fpga_mgr_register(dev, "Altera SOCFPGA FPGA Manager", | ||
| 161 | &socfpga_fpga_ops, priv); | ||
| 162 | } | ||
| 163 | |||
| 164 | static int socfpga_fpga_remove(struct platform_device *pdev) | ||
| 165 | { | ||
| 166 | fpga_mgr_unregister(&pdev->dev); | ||
| 167 | |||
| 168 | return 0; | ||
| 169 | } | ||
| 170 | |||
| 171 | |||
| 172 | The ops will implement whatever device specific register writes are needed to | ||
| 173 | do the programming sequence for this particular FPGA. These ops return 0 for | ||
| 174 | success or negative error codes otherwise. | ||
| 175 | |||
| 176 | The programming sequence is: | ||
| 177 | 1. .write_init | ||
| 178 | 2. .write or .write_sg (may be called once or multiple times) | ||
| 179 | 3. .write_complete | ||
| 180 | |||
| 181 | The .write_init function will prepare the FPGA to receive the image data. The | ||
| 182 | buffer passed into .write_init will be atmost .initial_header_size bytes long, | ||
| 183 | if the whole bitstream is not immediately available then the core code will | ||
| 184 | buffer up at least this much before starting. | ||
| 185 | |||
| 186 | The .write function writes a buffer to the FPGA. The buffer may be contain the | ||
| 187 | whole FPGA image or may be a smaller chunk of an FPGA image. In the latter | ||
| 188 | case, this function is called multiple times for successive chunks. This interface | ||
| 189 | is suitable for drivers which use PIO. | ||
| 190 | |||
| 191 | The .write_sg version behaves the same as .write except the input is a sg_table | ||
| 192 | scatter list. This interface is suitable for drivers which use DMA. | ||
| 193 | |||
| 194 | The .write_complete function is called after all the image has been written | ||
| 195 | to put the FPGA into operating mode. | ||
| 196 | |||
| 197 | The ops include a .state function which will read the hardware FPGA manager and | ||
| 198 | return a code of type enum fpga_mgr_states. It doesn't result in a change in | ||
| 199 | hardware state. | ||
diff --git a/Documentation/fpga/fpga-region.txt b/Documentation/fpga/fpga-region.txt deleted file mode 100644 index 139a02ba1ff6..000000000000 --- a/Documentation/fpga/fpga-region.txt +++ /dev/null | |||
| @@ -1,95 +0,0 @@ | |||
| 1 | FPGA Regions | ||
| 2 | |||
| 3 | Alan Tull 2017 | ||
| 4 | |||
| 5 | CONTENTS | ||
| 6 | - Introduction | ||
| 7 | - The FPGA region API | ||
| 8 | - Usage example | ||
| 9 | |||
| 10 | Introduction | ||
| 11 | ============ | ||
| 12 | |||
| 13 | This document is meant to be an brief overview of the FPGA region API usage. A | ||
| 14 | more conceptual look at regions can be found in [1]. | ||
| 15 | |||
| 16 | For the purposes of this API document, let's just say that a region associates | ||
| 17 | an FPGA Manager and a bridge (or bridges) with a reprogrammable region of an | ||
| 18 | FPGA or the whole FPGA. The API provides a way to register a region and to | ||
| 19 | program a region. | ||
| 20 | |||
| 21 | Currently the only layer above fpga-region.c in the kernel is the Device Tree | ||
| 22 | support (of-fpga-region.c) described in [1]. The DT support layer uses regions | ||
| 23 | to program the FPGA and then DT to handle enumeration. The common region code | ||
| 24 | is intended to be used by other schemes that have other ways of accomplishing | ||
| 25 | enumeration after programming. | ||
| 26 | |||
| 27 | An fpga-region can be set up to know the following things: | ||
| 28 | * which FPGA manager to use to do the programming | ||
| 29 | * which bridges to disable before programming and enable afterwards. | ||
| 30 | |||
| 31 | Additional info needed to program the FPGA image is passed in the struct | ||
| 32 | fpga_image_info [2] including: | ||
| 33 | * pointers to the image as either a scatter-gather buffer, a contiguous | ||
| 34 | buffer, or the name of firmware file | ||
| 35 | * flags indicating specifics such as whether the image if for partial | ||
| 36 | reconfiguration. | ||
| 37 | |||
| 38 | =================== | ||
| 39 | The FPGA region API | ||
| 40 | =================== | ||
| 41 | |||
| 42 | To register or unregister a region: | ||
| 43 | ----------------------------------- | ||
| 44 | |||
| 45 | int fpga_region_register(struct device *dev, | ||
| 46 | struct fpga_region *region); | ||
| 47 | int fpga_region_unregister(struct fpga_region *region); | ||
| 48 | |||
| 49 | An example of usage can be seen in the probe function of [3] | ||
| 50 | |||
| 51 | To program an FPGA: | ||
| 52 | ------------------- | ||
| 53 | int fpga_region_program_fpga(struct fpga_region *region); | ||
| 54 | |||
| 55 | This function operates on info passed in the fpga_image_info | ||
| 56 | (region->info). | ||
| 57 | |||
| 58 | This function will attempt to: | ||
| 59 | * lock the region's mutex | ||
| 60 | * lock the region's FPGA manager | ||
| 61 | * build a list of FPGA bridges if a method has been specified to do so | ||
| 62 | * disable the bridges | ||
| 63 | * program the FPGA | ||
| 64 | * re-enable the bridges | ||
| 65 | * release the locks | ||
| 66 | |||
| 67 | ============= | ||
| 68 | Usage example | ||
| 69 | ============= | ||
| 70 | |||
| 71 | First, allocate the info struct: | ||
| 72 | |||
| 73 | info = fpga_image_info_alloc(dev); | ||
| 74 | if (!info) | ||
| 75 | return -ENOMEM; | ||
| 76 | |||
| 77 | Set flags as needed, i.e. | ||
| 78 | |||
| 79 | info->flags |= FPGA_MGR_PARTIAL_RECONFIG; | ||
| 80 | |||
| 81 | Point to your FPGA image, such as: | ||
| 82 | |||
| 83 | info->sgt = &sgt; | ||
| 84 | |||
| 85 | Add info to region and do the programming: | ||
| 86 | |||
| 87 | region->info = info; | ||
| 88 | ret = fpga_region_program_fpga(region); | ||
| 89 | |||
| 90 | Then enumerate whatever hardware has appeared in the FPGA. | ||
| 91 | |||
| 92 | -- | ||
| 93 | [1] ../devicetree/bindings/fpga/fpga-region.txt | ||
| 94 | [2] ./fpga-mgr.txt | ||
| 95 | [3] ../../drivers/fpga/of-fpga-region.c | ||
diff --git a/Documentation/fpga/overview.txt b/Documentation/fpga/overview.txt deleted file mode 100644 index 0f1236e7e675..000000000000 --- a/Documentation/fpga/overview.txt +++ /dev/null | |||
| @@ -1,23 +0,0 @@ | |||
| 1 | Linux kernel FPGA support | ||
| 2 | |||
| 3 | Alan Tull 2017 | ||
| 4 | |||
| 5 | The main point of this project has been to separate the out the upper layers | ||
| 6 | that know when to reprogram a FPGA from the lower layers that know how to | ||
| 7 | reprogram a specific FPGA device. The intention is to make this manufacturer | ||
| 8 | agnostic, understanding that of course the FPGA images are very device specific | ||
| 9 | themselves. | ||
| 10 | |||
| 11 | The framework in the kernel includes: | ||
| 12 | * low level FPGA manager drivers that know how to program a specific device | ||
| 13 | * the fpga-mgr framework they are registered with | ||
| 14 | * low level FPGA bridge drivers for hard/soft bridges which are intended to | ||
| 15 | be disable during FPGA programming | ||
| 16 | * the fpga-bridge framework they are registered with | ||
| 17 | * the fpga-region framework which associates and controls managers and bridges | ||
| 18 | as reconfigurable regions | ||
| 19 | * the of-fpga-region support for reprogramming FPGAs when device tree overlays | ||
| 20 | are applied. | ||
| 21 | |||
| 22 | I would encourage you the user to add code that creates FPGA regions rather | ||
| 23 | that trying to control managers and bridges separately. | ||
diff --git a/Documentation/ioctl/ioctl-number.txt b/Documentation/ioctl/ioctl-number.txt index 27c1b7b78504..a12488d45c40 100644 --- a/Documentation/ioctl/ioctl-number.txt +++ b/Documentation/ioctl/ioctl-number.txt | |||
| @@ -328,6 +328,7 @@ Code Seq#(hex) Include File Comments | |||
| 328 | 0xCA 80-BF uapi/scsi/cxlflash_ioctl.h | 328 | 0xCA 80-BF uapi/scsi/cxlflash_ioctl.h |
| 329 | 0xCB 00-1F CBM serial IEC bus in development: | 329 | 0xCB 00-1F CBM serial IEC bus in development: |
| 330 | <mailto:michael.klein@puffin.lb.shuttle.de> | 330 | <mailto:michael.klein@puffin.lb.shuttle.de> |
| 331 | 0xCC 00-0F drivers/misc/ibmvmc.h pseries VMC driver | ||
| 331 | 0xCD 01 linux/reiserfs_fs.h | 332 | 0xCD 01 linux/reiserfs_fs.h |
| 332 | 0xCF 02 fs/cifs/ioctl.c | 333 | 0xCF 02 fs/cifs/ioctl.c |
| 333 | 0xDB 00-0F drivers/char/mwave/mwavepub.h | 334 | 0xDB 00-0F drivers/char/mwave/mwavepub.h |
diff --git a/Documentation/misc-devices/ibmvmc.rst b/Documentation/misc-devices/ibmvmc.rst new file mode 100644 index 000000000000..46ded79554d4 --- /dev/null +++ b/Documentation/misc-devices/ibmvmc.rst | |||
| @@ -0,0 +1,226 @@ | |||
| 1 | .. SPDX-License-Identifier: GPL-2.0+ | ||
| 2 | ====================================================== | ||
| 3 | IBM Virtual Management Channel Kernel Driver (IBMVMC) | ||
| 4 | ====================================================== | ||
| 5 | |||
| 6 | :Authors: | ||
| 7 | Dave Engebretsen <engebret@us.ibm.com>, | ||
| 8 | Adam Reznechek <adreznec@linux.vnet.ibm.com>, | ||
| 9 | Steven Royer <seroyer@linux.vnet.ibm.com>, | ||
| 10 | Bryant G. Ly <bryantly@linux.vnet.ibm.com>, | ||
| 11 | |||
| 12 | Introduction | ||
| 13 | ============ | ||
| 14 | |||
| 15 | Note: Knowledge of virtualization technology is required to understand | ||
| 16 | this document. | ||
| 17 | |||
| 18 | A good reference document would be: | ||
| 19 | |||
| 20 | https://openpowerfoundation.org/wp-content/uploads/2016/05/LoPAPR_DRAFT_v11_24March2016_cmt1.pdf | ||
| 21 | |||
| 22 | The Virtual Management Channel (VMC) is a logical device which provides an | ||
| 23 | interface between the hypervisor and a management partition. This interface | ||
| 24 | is like a message passing interface. This management partition is intended | ||
| 25 | to provide an alternative to systems that use a Hardware Management | ||
| 26 | Console (HMC) - based system management. | ||
| 27 | |||
| 28 | The primary hardware management solution that is developed by IBM relies | ||
| 29 | on an appliance server named the Hardware Management Console (HMC), | ||
| 30 | packaged as an external tower or rack-mounted personal computer. In a | ||
| 31 | Power Systems environment, a single HMC can manage multiple POWER | ||
| 32 | processor-based systems. | ||
| 33 | |||
| 34 | Management Application | ||
| 35 | ---------------------- | ||
| 36 | |||
| 37 | In the management partition, a management application exists which enables | ||
| 38 | a system administrator to configure the system’s partitioning | ||
| 39 | characteristics via a command line interface (CLI) or Representational | ||
| 40 | State Transfer Application (REST API's). | ||
| 41 | |||
| 42 | The management application runs on a Linux logical partition on a | ||
| 43 | POWER8 or newer processor-based server that is virtualized by PowerVM. | ||
| 44 | System configuration, maintenance, and control functions which | ||
| 45 | traditionally require an HMC can be implemented in the management | ||
| 46 | application using a combination of HMC to hypervisor interfaces and | ||
| 47 | existing operating system methods. This tool provides a subset of the | ||
| 48 | functions implemented by the HMC and enables basic partition configuration. | ||
| 49 | The set of HMC to hypervisor messages supported by the management | ||
| 50 | application component are passed to the hypervisor over a VMC interface, | ||
| 51 | which is defined below. | ||
| 52 | |||
| 53 | The VMC enables the management partition to provide basic partitioning | ||
| 54 | functions: | ||
| 55 | |||
| 56 | - Logical Partitioning Configuration | ||
| 57 | - Start, and stop actions for individual partitions | ||
| 58 | - Display of partition status | ||
| 59 | - Management of virtual Ethernet | ||
| 60 | - Management of virtual Storage | ||
| 61 | - Basic system management | ||
| 62 | |||
| 63 | Virtual Management Channel (VMC) | ||
| 64 | -------------------------------- | ||
| 65 | |||
| 66 | A logical device, called the Virtual Management Channel (VMC), is defined | ||
| 67 | for communicating between the management application and the hypervisor. It | ||
| 68 | basically creates the pipes that enable virtualization management | ||
| 69 | software. This device is presented to a designated management partition as | ||
| 70 | a virtual device. | ||
| 71 | |||
| 72 | This communication device uses Command/Response Queue (CRQ) and the | ||
| 73 | Remote Direct Memory Access (RDMA) interfaces. A three-way handshake is | ||
| 74 | defined that must take place to establish that both the hypervisor and | ||
| 75 | management partition sides of the channel are running prior to | ||
| 76 | sending/receiving any of the protocol messages. | ||
| 77 | |||
| 78 | This driver also utilizes Transport Event CRQs. CRQ messages are sent | ||
| 79 | when the hypervisor detects one of the peer partitions has abnormally | ||
| 80 | terminated, or one side has called H_FREE_CRQ to close their CRQ. | ||
| 81 | Two new classes of CRQ messages are introduced for the VMC device. VMC | ||
| 82 | Administrative messages are used for each partition using the VMC to | ||
| 83 | communicate capabilities to their partner. HMC Interface messages are used | ||
| 84 | for the actual flow of HMC messages between the management partition and | ||
| 85 | the hypervisor. As most HMC messages far exceed the size of a CRQ buffer, | ||
| 86 | a virtual DMA (RMDA) of the HMC message data is done prior to each HMC | ||
| 87 | Interface CRQ message. Only the management partition drives RDMA | ||
| 88 | operations; hypervisors never directly cause the movement of message data. | ||
| 89 | |||
| 90 | |||
| 91 | Terminology | ||
| 92 | ----------- | ||
| 93 | RDMA | ||
| 94 | Remote Direct Memory Access is DMA transfer from the server to its | ||
| 95 | client or from the server to its partner partition. DMA refers | ||
| 96 | to both physical I/O to and from memory operations and to memory | ||
| 97 | to memory move operations. | ||
| 98 | CRQ | ||
| 99 | Command/Response Queue a facility which is used to communicate | ||
| 100 | between partner partitions. Transport events which are signaled | ||
| 101 | from the hypervisor to partition are also reported in this queue. | ||
| 102 | |||
| 103 | Example Management Partition VMC Driver Interface | ||
| 104 | ================================================= | ||
| 105 | |||
| 106 | This section provides an example for the management application | ||
| 107 | implementation where a device driver is used to interface to the VMC | ||
| 108 | device. This driver consists of a new device, for example /dev/ibmvmc, | ||
| 109 | which provides interfaces to open, close, read, write, and perform | ||
| 110 | ioctl’s against the VMC device. | ||
| 111 | |||
| 112 | VMC Interface Initialization | ||
| 113 | ---------------------------- | ||
| 114 | |||
| 115 | The device driver is responsible for initializing the VMC when the driver | ||
| 116 | is loaded. It first creates and initializes the CRQ. Next, an exchange of | ||
| 117 | VMC capabilities is performed to indicate the code version and number of | ||
| 118 | resources available in both the management partition and the hypervisor. | ||
| 119 | Finally, the hypervisor requests that the management partition create an | ||
| 120 | initial pool of VMC buffers, one buffer for each possible HMC connection, | ||
| 121 | which will be used for management application session initialization. | ||
| 122 | Prior to completion of this initialization sequence, the device returns | ||
| 123 | EBUSY to open() calls. EIO is returned for all open() failures. | ||
| 124 | |||
| 125 | :: | ||
| 126 | |||
| 127 | Management Partition Hypervisor | ||
| 128 | CRQ INIT | ||
| 129 | ----------------------------------------> | ||
| 130 | CRQ INIT COMPLETE | ||
| 131 | <---------------------------------------- | ||
| 132 | CAPABILITIES | ||
| 133 | ----------------------------------------> | ||
| 134 | CAPABILITIES RESPONSE | ||
| 135 | <---------------------------------------- | ||
| 136 | ADD BUFFER (HMC IDX=0,1,..) _ | ||
| 137 | <---------------------------------------- | | ||
| 138 | ADD BUFFER RESPONSE | - Perform # HMCs Iterations | ||
| 139 | ----------------------------------------> - | ||
| 140 | |||
| 141 | VMC Interface Open | ||
| 142 | ------------------ | ||
| 143 | |||
| 144 | After the basic VMC channel has been initialized, an HMC session level | ||
| 145 | connection can be established. The application layer performs an open() to | ||
| 146 | the VMC device and executes an ioctl() against it, indicating the HMC ID | ||
| 147 | (32 bytes of data) for this session. If the VMC device is in an invalid | ||
| 148 | state, EIO will be returned for the ioctl(). The device driver creates a | ||
| 149 | new HMC session value (ranging from 1 to 255) and HMC index value (starting | ||
| 150 | at index 0 and ranging to 254) for this HMC ID. The driver then does an | ||
| 151 | RDMA of the HMC ID to the hypervisor, and then sends an Interface Open | ||
| 152 | message to the hypervisor to establish the session over the VMC. After the | ||
| 153 | hypervisor receives this information, it sends Add Buffer messages to the | ||
| 154 | management partition to seed an initial pool of buffers for the new HMC | ||
| 155 | connection. Finally, the hypervisor sends an Interface Open Response | ||
| 156 | message, to indicate that it is ready for normal runtime messaging. The | ||
| 157 | following illustrates this VMC flow: | ||
| 158 | |||
| 159 | :: | ||
| 160 | |||
| 161 | Management Partition Hypervisor | ||
| 162 | RDMA HMC ID | ||
| 163 | ----------------------------------------> | ||
| 164 | Interface Open | ||
| 165 | ----------------------------------------> | ||
| 166 | Add Buffer _ | ||
| 167 | <---------------------------------------- | | ||
| 168 | Add Buffer Response | - Perform N Iterations | ||
| 169 | ----------------------------------------> - | ||
| 170 | Interface Open Response | ||
| 171 | <---------------------------------------- | ||
| 172 | |||
| 173 | VMC Interface Runtime | ||
| 174 | --------------------- | ||
| 175 | |||
| 176 | During normal runtime, the management application and the hypervisor | ||
| 177 | exchange HMC messages via the Signal VMC message and RDMA operations. When | ||
| 178 | sending data to the hypervisor, the management application performs a | ||
| 179 | write() to the VMC device, and the driver RDMA’s the data to the hypervisor | ||
| 180 | and then sends a Signal Message. If a write() is attempted before VMC | ||
| 181 | device buffers have been made available by the hypervisor, or no buffers | ||
| 182 | are currently available, EBUSY is returned in response to the write(). A | ||
| 183 | write() will return EIO for all other errors, such as an invalid device | ||
| 184 | state. When the hypervisor sends a message to the management, the data is | ||
| 185 | put into a VMC buffer and an Signal Message is sent to the VMC driver in | ||
| 186 | the management partition. The driver RDMA’s the buffer into the partition | ||
| 187 | and passes the data up to the appropriate management application via a | ||
| 188 | read() to the VMC device. The read() request blocks if there is no buffer | ||
| 189 | available to read. The management application may use select() to wait for | ||
| 190 | the VMC device to become ready with data to read. | ||
| 191 | |||
| 192 | :: | ||
| 193 | |||
| 194 | Management Partition Hypervisor | ||
| 195 | MSG RDMA | ||
| 196 | ----------------------------------------> | ||
| 197 | SIGNAL MSG | ||
| 198 | ----------------------------------------> | ||
| 199 | SIGNAL MSG | ||
| 200 | <---------------------------------------- | ||
| 201 | MSG RDMA | ||
| 202 | <---------------------------------------- | ||
| 203 | |||
| 204 | VMC Interface Close | ||
| 205 | ------------------- | ||
| 206 | |||
| 207 | HMC session level connections are closed by the management partition when | ||
| 208 | the application layer performs a close() against the device. This action | ||
| 209 | results in an Interface Close message flowing to the hypervisor, which | ||
| 210 | causes the session to be terminated. The device driver must free any | ||
| 211 | storage allocated for buffers for this HMC connection. | ||
| 212 | |||
| 213 | :: | ||
| 214 | |||
| 215 | Management Partition Hypervisor | ||
| 216 | INTERFACE CLOSE | ||
| 217 | ----------------------------------------> | ||
| 218 | INTERFACE CLOSE RESPONSE | ||
| 219 | <---------------------------------------- | ||
| 220 | |||
| 221 | Additional Information | ||
| 222 | ====================== | ||
| 223 | |||
| 224 | For more information on the documentation for CRQ Messages, VMC Messages, | ||
| 225 | HMC interface Buffers, and signal messages please refer to the Linux on | ||
| 226 | Power Architecture Platform Reference. Section F. | ||
diff --git a/Documentation/trace/coresight-cpu-debug.txt b/Documentation/trace/coresight-cpu-debug.txt index 2b9b51cd501e..89ab09e78e8d 100644 --- a/Documentation/trace/coresight-cpu-debug.txt +++ b/Documentation/trace/coresight-cpu-debug.txt | |||
| @@ -177,11 +177,11 @@ Here is an example of the debugging output format: | |||
| 177 | ARM external debug module: | 177 | ARM external debug module: |
| 178 | coresight-cpu-debug 850000.debug: CPU[0]: | 178 | coresight-cpu-debug 850000.debug: CPU[0]: |
| 179 | coresight-cpu-debug 850000.debug: EDPRSR: 00000001 (Power:On DLK:Unlock) | 179 | coresight-cpu-debug 850000.debug: EDPRSR: 00000001 (Power:On DLK:Unlock) |
| 180 | coresight-cpu-debug 850000.debug: EDPCSR: [<ffff00000808e9bc>] handle_IPI+0x174/0x1d8 | 180 | coresight-cpu-debug 850000.debug: EDPCSR: handle_IPI+0x174/0x1d8 |
| 181 | coresight-cpu-debug 850000.debug: EDCIDSR: 00000000 | 181 | coresight-cpu-debug 850000.debug: EDCIDSR: 00000000 |
| 182 | coresight-cpu-debug 850000.debug: EDVIDSR: 90000000 (State:Non-secure Mode:EL1/0 Width:64bits VMID:0) | 182 | coresight-cpu-debug 850000.debug: EDVIDSR: 90000000 (State:Non-secure Mode:EL1/0 Width:64bits VMID:0) |
| 183 | coresight-cpu-debug 852000.debug: CPU[1]: | 183 | coresight-cpu-debug 852000.debug: CPU[1]: |
| 184 | coresight-cpu-debug 852000.debug: EDPRSR: 00000001 (Power:On DLK:Unlock) | 184 | coresight-cpu-debug 852000.debug: EDPRSR: 00000001 (Power:On DLK:Unlock) |
| 185 | coresight-cpu-debug 852000.debug: EDPCSR: [<ffff0000087fab34>] debug_notifier_call+0x23c/0x358 | 185 | coresight-cpu-debug 852000.debug: EDPCSR: debug_notifier_call+0x23c/0x358 |
| 186 | coresight-cpu-debug 852000.debug: EDCIDSR: 00000000 | 186 | coresight-cpu-debug 852000.debug: EDCIDSR: 00000000 |
| 187 | coresight-cpu-debug 852000.debug: EDVIDSR: 90000000 (State:Non-secure Mode:EL1/0 Width:64bits VMID:0) | 187 | coresight-cpu-debug 852000.debug: EDVIDSR: 90000000 (State:Non-secure Mode:EL1/0 Width:64bits VMID:0) |
diff --git a/MAINTAINERS b/MAINTAINERS index 756cbfaad7f9..5c374d97c967 100644 --- a/MAINTAINERS +++ b/MAINTAINERS | |||
| @@ -5586,6 +5586,7 @@ S: Maintained | |||
| 5586 | T: git git://git.kernel.org/pub/scm/linux/kernel/git/atull/linux-fpga.git | 5586 | T: git git://git.kernel.org/pub/scm/linux/kernel/git/atull/linux-fpga.git |
| 5587 | Q: http://patchwork.kernel.org/project/linux-fpga/list/ | 5587 | Q: http://patchwork.kernel.org/project/linux-fpga/list/ |
| 5588 | F: Documentation/fpga/ | 5588 | F: Documentation/fpga/ |
| 5589 | F: Documentation/driver-api/fpga/ | ||
| 5589 | F: Documentation/devicetree/bindings/fpga/ | 5590 | F: Documentation/devicetree/bindings/fpga/ |
| 5590 | F: drivers/fpga/ | 5591 | F: drivers/fpga/ |
| 5591 | F: include/linux/fpga/ | 5592 | F: include/linux/fpga/ |
| @@ -6773,6 +6774,12 @@ L: linux-scsi@vger.kernel.org | |||
| 6773 | S: Supported | 6774 | S: Supported |
| 6774 | F: drivers/scsi/ibmvscsi/ibmvfc* | 6775 | F: drivers/scsi/ibmvscsi/ibmvfc* |
| 6775 | 6776 | ||
| 6777 | IBM Power Virtual Management Channel Driver | ||
| 6778 | M: Bryant G. Ly <bryantly@linux.vnet.ibm.com> | ||
| 6779 | M: Steven Royer <seroyer@linux.vnet.ibm.com> | ||
| 6780 | S: Supported | ||
| 6781 | F: drivers/misc/ibmvmc.* | ||
| 6782 | |||
| 6776 | IBM Power Virtual SCSI Device Drivers | 6783 | IBM Power Virtual SCSI Device Drivers |
| 6777 | M: Tyrel Datwyler <tyreld@linux.vnet.ibm.com> | 6784 | M: Tyrel Datwyler <tyreld@linux.vnet.ibm.com> |
| 6778 | L: linux-scsi@vger.kernel.org | 6785 | L: linux-scsi@vger.kernel.org |
| @@ -13136,7 +13143,7 @@ F: include/uapi/sound/ | |||
| 13136 | F: sound/ | 13143 | F: sound/ |
| 13137 | 13144 | ||
| 13138 | SOUND - COMPRESSED AUDIO | 13145 | SOUND - COMPRESSED AUDIO |
| 13139 | M: Vinod Koul <vinod.koul@intel.com> | 13146 | M: Vinod Koul <vkoul@kernel.org> |
| 13140 | L: alsa-devel@alsa-project.org (moderated for non-subscribers) | 13147 | L: alsa-devel@alsa-project.org (moderated for non-subscribers) |
| 13141 | T: git git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound.git | 13148 | T: git git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound.git |
| 13142 | S: Supported | 13149 | S: Supported |
diff --git a/arch/powerpc/include/asm/hvcall.h b/arch/powerpc/include/asm/hvcall.h index 2e2dddab5d65..662c8347d699 100644 --- a/arch/powerpc/include/asm/hvcall.h +++ b/arch/powerpc/include/asm/hvcall.h | |||
| @@ -279,6 +279,7 @@ | |||
| 279 | #define H_GET_MPP_X 0x314 | 279 | #define H_GET_MPP_X 0x314 |
| 280 | #define H_SET_MODE 0x31C | 280 | #define H_SET_MODE 0x31C |
| 281 | #define H_CLEAR_HPT 0x358 | 281 | #define H_CLEAR_HPT 0x358 |
| 282 | #define H_REQUEST_VMC 0x360 | ||
| 282 | #define H_RESIZE_HPT_PREPARE 0x36C | 283 | #define H_RESIZE_HPT_PREPARE 0x36C |
| 283 | #define H_RESIZE_HPT_COMMIT 0x370 | 284 | #define H_RESIZE_HPT_COMMIT 0x370 |
| 284 | #define H_REGISTER_PROC_TBL 0x37C | 285 | #define H_REGISTER_PROC_TBL 0x37C |
diff --git a/drivers/amba/bus.c b/drivers/amba/bus.c index 0cf51d57e4ae..3ece711a6a17 100644 --- a/drivers/amba/bus.c +++ b/drivers/amba/bus.c | |||
| @@ -102,8 +102,8 @@ static ssize_t driver_override_store(struct device *_dev, | |||
| 102 | if (strlen(driver_override)) { | 102 | if (strlen(driver_override)) { |
| 103 | dev->driver_override = driver_override; | 103 | dev->driver_override = driver_override; |
| 104 | } else { | 104 | } else { |
| 105 | kfree(driver_override); | 105 | kfree(driver_override); |
| 106 | dev->driver_override = NULL; | 106 | dev->driver_override = NULL; |
| 107 | } | 107 | } |
| 108 | device_unlock(_dev); | 108 | device_unlock(_dev); |
| 109 | 109 | ||
diff --git a/drivers/android/Kconfig b/drivers/android/Kconfig index 7dce3795b887..ee4880bfdcdc 100644 --- a/drivers/android/Kconfig +++ b/drivers/android/Kconfig | |||
| @@ -10,7 +10,7 @@ if ANDROID | |||
| 10 | 10 | ||
| 11 | config ANDROID_BINDER_IPC | 11 | config ANDROID_BINDER_IPC |
| 12 | bool "Android Binder IPC Driver" | 12 | bool "Android Binder IPC Driver" |
| 13 | depends on MMU | 13 | depends on MMU && !M68K |
| 14 | default n | 14 | default n |
| 15 | ---help--- | 15 | ---help--- |
| 16 | Binder is used in Android for both communication between processes, | 16 | Binder is used in Android for both communication between processes, |
| @@ -32,19 +32,6 @@ config ANDROID_BINDER_DEVICES | |||
| 32 | created. Each binder device has its own context manager, and is | 32 | created. Each binder device has its own context manager, and is |
| 33 | therefore logically separated from the other devices. | 33 | therefore logically separated from the other devices. |
| 34 | 34 | ||
| 35 | config ANDROID_BINDER_IPC_32BIT | ||
| 36 | bool "Use old (Android 4.4 and earlier) 32-bit binder API" | ||
| 37 | depends on !64BIT && ANDROID_BINDER_IPC | ||
| 38 | default y | ||
| 39 | ---help--- | ||
| 40 | The Binder API has been changed to support both 32 and 64bit | ||
| 41 | applications in a mixed environment. | ||
| 42 | |||
| 43 | Enable this to support an old 32-bit Android user-space (v4.4 and | ||
| 44 | earlier). | ||
| 45 | |||
| 46 | Note that enabling this will break newer Android user-space. | ||
| 47 | |||
| 48 | config ANDROID_BINDER_IPC_SELFTEST | 35 | config ANDROID_BINDER_IPC_SELFTEST |
| 49 | bool "Android Binder IPC Driver Selftest" | 36 | bool "Android Binder IPC Driver Selftest" |
| 50 | depends on ANDROID_BINDER_IPC | 37 | depends on ANDROID_BINDER_IPC |
diff --git a/drivers/android/binder.c b/drivers/android/binder.c index e578eee31589..95283f3bb51c 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c | |||
| @@ -72,10 +72,6 @@ | |||
| 72 | #include <linux/security.h> | 72 | #include <linux/security.h> |
| 73 | #include <linux/spinlock.h> | 73 | #include <linux/spinlock.h> |
| 74 | 74 | ||
| 75 | #ifdef CONFIG_ANDROID_BINDER_IPC_32BIT | ||
| 76 | #define BINDER_IPC_32BIT 1 | ||
| 77 | #endif | ||
| 78 | |||
| 79 | #include <uapi/linux/android/binder.h> | 75 | #include <uapi/linux/android/binder.h> |
| 80 | #include "binder_alloc.h" | 76 | #include "binder_alloc.h" |
| 81 | #include "binder_trace.h" | 77 | #include "binder_trace.h" |
| @@ -2058,8 +2054,8 @@ static size_t binder_validate_object(struct binder_buffer *buffer, u64 offset) | |||
| 2058 | struct binder_object_header *hdr; | 2054 | struct binder_object_header *hdr; |
| 2059 | size_t object_size = 0; | 2055 | size_t object_size = 0; |
| 2060 | 2056 | ||
| 2061 | if (offset > buffer->data_size - sizeof(*hdr) || | 2057 | if (buffer->data_size < sizeof(*hdr) || |
| 2062 | buffer->data_size < sizeof(*hdr) || | 2058 | offset > buffer->data_size - sizeof(*hdr) || |
| 2063 | !IS_ALIGNED(offset, sizeof(u32))) | 2059 | !IS_ALIGNED(offset, sizeof(u32))) |
| 2064 | return 0; | 2060 | return 0; |
| 2065 | 2061 | ||
| @@ -3925,10 +3921,11 @@ retry: | |||
| 3925 | binder_inner_proc_unlock(proc); | 3921 | binder_inner_proc_unlock(proc); |
| 3926 | if (put_user(e->cmd, (uint32_t __user *)ptr)) | 3922 | if (put_user(e->cmd, (uint32_t __user *)ptr)) |
| 3927 | return -EFAULT; | 3923 | return -EFAULT; |
| 3924 | cmd = e->cmd; | ||
| 3928 | e->cmd = BR_OK; | 3925 | e->cmd = BR_OK; |
| 3929 | ptr += sizeof(uint32_t); | 3926 | ptr += sizeof(uint32_t); |
| 3930 | 3927 | ||
| 3931 | binder_stat_br(proc, thread, e->cmd); | 3928 | binder_stat_br(proc, thread, cmd); |
| 3932 | } break; | 3929 | } break; |
| 3933 | case BINDER_WORK_TRANSACTION_COMPLETE: { | 3930 | case BINDER_WORK_TRANSACTION_COMPLETE: { |
| 3934 | binder_inner_proc_unlock(proc); | 3931 | binder_inner_proc_unlock(proc); |
| @@ -4696,7 +4693,7 @@ static void binder_vma_close(struct vm_area_struct *vma) | |||
| 4696 | binder_defer_work(proc, BINDER_DEFERRED_PUT_FILES); | 4693 | binder_defer_work(proc, BINDER_DEFERRED_PUT_FILES); |
| 4697 | } | 4694 | } |
| 4698 | 4695 | ||
| 4699 | static int binder_vm_fault(struct vm_fault *vmf) | 4696 | static vm_fault_t binder_vm_fault(struct vm_fault *vmf) |
| 4700 | { | 4697 | { |
| 4701 | return VM_FAULT_SIGBUS; | 4698 | return VM_FAULT_SIGBUS; |
| 4702 | } | 4699 | } |
| @@ -4730,7 +4727,9 @@ static int binder_mmap(struct file *filp, struct vm_area_struct *vma) | |||
| 4730 | failure_string = "bad vm_flags"; | 4727 | failure_string = "bad vm_flags"; |
| 4731 | goto err_bad_arg; | 4728 | goto err_bad_arg; |
| 4732 | } | 4729 | } |
| 4733 | vma->vm_flags = (vma->vm_flags | VM_DONTCOPY) & ~VM_MAYWRITE; | 4730 | vma->vm_flags |= VM_DONTCOPY | VM_MIXEDMAP; |
| 4731 | vma->vm_flags &= ~VM_MAYWRITE; | ||
| 4732 | |||
| 4734 | vma->vm_ops = &binder_vm_ops; | 4733 | vma->vm_ops = &binder_vm_ops; |
| 4735 | vma->vm_private_data = proc; | 4734 | vma->vm_private_data = proc; |
| 4736 | 4735 | ||
diff --git a/drivers/android/binder_alloc.c b/drivers/android/binder_alloc.c index 5a426c877dfb..4f382d51def1 100644 --- a/drivers/android/binder_alloc.c +++ b/drivers/android/binder_alloc.c | |||
| @@ -219,7 +219,7 @@ static int binder_update_page_range(struct binder_alloc *alloc, int allocate, | |||
| 219 | mm = alloc->vma_vm_mm; | 219 | mm = alloc->vma_vm_mm; |
| 220 | 220 | ||
| 221 | if (mm) { | 221 | if (mm) { |
| 222 | down_write(&mm->mmap_sem); | 222 | down_read(&mm->mmap_sem); |
| 223 | vma = alloc->vma; | 223 | vma = alloc->vma; |
| 224 | } | 224 | } |
| 225 | 225 | ||
| @@ -288,7 +288,7 @@ static int binder_update_page_range(struct binder_alloc *alloc, int allocate, | |||
| 288 | /* vm_insert_page does not seem to increment the refcount */ | 288 | /* vm_insert_page does not seem to increment the refcount */ |
| 289 | } | 289 | } |
| 290 | if (mm) { | 290 | if (mm) { |
| 291 | up_write(&mm->mmap_sem); | 291 | up_read(&mm->mmap_sem); |
| 292 | mmput(mm); | 292 | mmput(mm); |
| 293 | } | 293 | } |
| 294 | return 0; | 294 | return 0; |
| @@ -321,7 +321,7 @@ err_page_ptr_cleared: | |||
| 321 | } | 321 | } |
| 322 | err_no_vma: | 322 | err_no_vma: |
| 323 | if (mm) { | 323 | if (mm) { |
| 324 | up_write(&mm->mmap_sem); | 324 | up_read(&mm->mmap_sem); |
| 325 | mmput(mm); | 325 | mmput(mm); |
| 326 | } | 326 | } |
| 327 | return vma ? -ENOMEM : -ESRCH; | 327 | return vma ? -ENOMEM : -ESRCH; |
diff --git a/drivers/char/mspec.c b/drivers/char/mspec.c index 7b75669d3670..058876b55b09 100644 --- a/drivers/char/mspec.c +++ b/drivers/char/mspec.c | |||
| @@ -191,7 +191,7 @@ mspec_close(struct vm_area_struct *vma) | |||
| 191 | * | 191 | * |
| 192 | * Creates a mspec page and maps it to user space. | 192 | * Creates a mspec page and maps it to user space. |
| 193 | */ | 193 | */ |
| 194 | static int | 194 | static vm_fault_t |
| 195 | mspec_fault(struct vm_fault *vmf) | 195 | mspec_fault(struct vm_fault *vmf) |
| 196 | { | 196 | { |
| 197 | unsigned long paddr, maddr; | 197 | unsigned long paddr, maddr; |
| @@ -223,14 +223,7 @@ mspec_fault(struct vm_fault *vmf) | |||
| 223 | 223 | ||
| 224 | pfn = paddr >> PAGE_SHIFT; | 224 | pfn = paddr >> PAGE_SHIFT; |
| 225 | 225 | ||
| 226 | /* | 226 | return vmf_insert_pfn(vmf->vma, vmf->address, pfn); |
| 227 | * vm_insert_pfn can fail with -EBUSY, but in that case it will | ||
| 228 | * be because another thread has installed the pte first, so it | ||
| 229 | * is no problem. | ||
| 230 | */ | ||
| 231 | vm_insert_pfn(vmf->vma, vmf->address, pfn); | ||
| 232 | |||
| 233 | return VM_FAULT_NOPAGE; | ||
| 234 | } | 227 | } |
| 235 | 228 | ||
| 236 | static const struct vm_operations_struct mspec_vm_ops = { | 229 | static const struct vm_operations_struct mspec_vm_ops = { |
diff --git a/drivers/firmware/google/Kconfig b/drivers/firmware/google/Kconfig index f16b381a569c..a456a000048b 100644 --- a/drivers/firmware/google/Kconfig +++ b/drivers/firmware/google/Kconfig | |||
| @@ -55,6 +55,14 @@ config GOOGLE_MEMCONSOLE_X86_LEGACY | |||
| 55 | the EBDA on Google servers. If found, this log is exported to | 55 | the EBDA on Google servers. If found, this log is exported to |
| 56 | userland in the file /sys/firmware/log. | 56 | userland in the file /sys/firmware/log. |
| 57 | 57 | ||
| 58 | config GOOGLE_FRAMEBUFFER_COREBOOT | ||
| 59 | tristate "Coreboot Framebuffer" | ||
| 60 | depends on FB_SIMPLE | ||
| 61 | depends on GOOGLE_COREBOOT_TABLE | ||
| 62 | help | ||
| 63 | This option enables the kernel to search for a framebuffer in | ||
| 64 | the coreboot table. If found, it is registered with simplefb. | ||
| 65 | |||
| 58 | config GOOGLE_MEMCONSOLE_COREBOOT | 66 | config GOOGLE_MEMCONSOLE_COREBOOT |
| 59 | tristate "Firmware Memory Console" | 67 | tristate "Firmware Memory Console" |
| 60 | depends on GOOGLE_COREBOOT_TABLE | 68 | depends on GOOGLE_COREBOOT_TABLE |
diff --git a/drivers/firmware/google/Makefile b/drivers/firmware/google/Makefile index dcd3675efcfc..d0b3fba96194 100644 --- a/drivers/firmware/google/Makefile +++ b/drivers/firmware/google/Makefile | |||
| @@ -4,6 +4,7 @@ obj-$(CONFIG_GOOGLE_SMI) += gsmi.o | |||
| 4 | obj-$(CONFIG_GOOGLE_COREBOOT_TABLE) += coreboot_table.o | 4 | obj-$(CONFIG_GOOGLE_COREBOOT_TABLE) += coreboot_table.o |
| 5 | obj-$(CONFIG_GOOGLE_COREBOOT_TABLE_ACPI) += coreboot_table-acpi.o | 5 | obj-$(CONFIG_GOOGLE_COREBOOT_TABLE_ACPI) += coreboot_table-acpi.o |
| 6 | obj-$(CONFIG_GOOGLE_COREBOOT_TABLE_OF) += coreboot_table-of.o | 6 | obj-$(CONFIG_GOOGLE_COREBOOT_TABLE_OF) += coreboot_table-of.o |
| 7 | obj-$(CONFIG_GOOGLE_FRAMEBUFFER_COREBOOT) += framebuffer-coreboot.o | ||
| 7 | obj-$(CONFIG_GOOGLE_MEMCONSOLE) += memconsole.o | 8 | obj-$(CONFIG_GOOGLE_MEMCONSOLE) += memconsole.o |
| 8 | obj-$(CONFIG_GOOGLE_MEMCONSOLE_COREBOOT) += memconsole-coreboot.o | 9 | obj-$(CONFIG_GOOGLE_MEMCONSOLE_COREBOOT) += memconsole-coreboot.o |
| 9 | obj-$(CONFIG_GOOGLE_MEMCONSOLE_X86_LEGACY) += memconsole-x86-legacy.o | 10 | obj-$(CONFIG_GOOGLE_MEMCONSOLE_X86_LEGACY) += memconsole-x86-legacy.o |
diff --git a/drivers/firmware/google/coreboot_table-acpi.c b/drivers/firmware/google/coreboot_table-acpi.c index fb98db2d20e2..77197fe3d42f 100644 --- a/drivers/firmware/google/coreboot_table-acpi.c +++ b/drivers/firmware/google/coreboot_table-acpi.c | |||
| @@ -53,7 +53,7 @@ static int coreboot_table_acpi_probe(struct platform_device *pdev) | |||
| 53 | if (!ptr) | 53 | if (!ptr) |
| 54 | return -ENOMEM; | 54 | return -ENOMEM; |
| 55 | 55 | ||
| 56 | return coreboot_table_init(ptr); | 56 | return coreboot_table_init(&pdev->dev, ptr); |
| 57 | } | 57 | } |
| 58 | 58 | ||
| 59 | static int coreboot_table_acpi_remove(struct platform_device *pdev) | 59 | static int coreboot_table_acpi_remove(struct platform_device *pdev) |
diff --git a/drivers/firmware/google/coreboot_table-of.c b/drivers/firmware/google/coreboot_table-of.c index 727acdc83e83..f15bf404c579 100644 --- a/drivers/firmware/google/coreboot_table-of.c +++ b/drivers/firmware/google/coreboot_table-of.c | |||
| @@ -34,7 +34,7 @@ static int coreboot_table_of_probe(struct platform_device *pdev) | |||
| 34 | if (!ptr) | 34 | if (!ptr) |
| 35 | return -ENOMEM; | 35 | return -ENOMEM; |
| 36 | 36 | ||
| 37 | return coreboot_table_init(ptr); | 37 | return coreboot_table_init(&pdev->dev, ptr); |
| 38 | } | 38 | } |
| 39 | 39 | ||
| 40 | static int coreboot_table_of_remove(struct platform_device *pdev) | 40 | static int coreboot_table_of_remove(struct platform_device *pdev) |
diff --git a/drivers/firmware/google/coreboot_table.c b/drivers/firmware/google/coreboot_table.c index 0019d3ec18dd..19db5709ae28 100644 --- a/drivers/firmware/google/coreboot_table.c +++ b/drivers/firmware/google/coreboot_table.c | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | * Module providing coreboot table access. | 4 | * Module providing coreboot table access. |
| 5 | * | 5 | * |
| 6 | * Copyright 2017 Google Inc. | 6 | * Copyright 2017 Google Inc. |
| 7 | * Copyright 2017 Samuel Holland <samuel@sholland.org> | ||
| 7 | * | 8 | * |
| 8 | * This program is free software; you can redistribute it and/or modify | 9 | * This program is free software; you can redistribute it and/or modify |
| 9 | * it under the terms of the GNU General Public License v2.0 as published by | 10 | * it under the terms of the GNU General Public License v2.0 as published by |
| @@ -15,37 +16,96 @@ | |||
| 15 | * GNU General Public License for more details. | 16 | * GNU General Public License for more details. |
| 16 | */ | 17 | */ |
| 17 | 18 | ||
| 19 | #include <linux/device.h> | ||
| 18 | #include <linux/err.h> | 20 | #include <linux/err.h> |
| 19 | #include <linux/init.h> | 21 | #include <linux/init.h> |
| 20 | #include <linux/io.h> | 22 | #include <linux/io.h> |
| 21 | #include <linux/kernel.h> | 23 | #include <linux/kernel.h> |
| 22 | #include <linux/module.h> | 24 | #include <linux/module.h> |
| 25 | #include <linux/slab.h> | ||
| 23 | 26 | ||
| 24 | #include "coreboot_table.h" | 27 | #include "coreboot_table.h" |
| 25 | 28 | ||
| 26 | struct coreboot_table_entry { | 29 | #define CB_DEV(d) container_of(d, struct coreboot_device, dev) |
| 27 | u32 tag; | 30 | #define CB_DRV(d) container_of(d, struct coreboot_driver, drv) |
| 28 | u32 size; | ||
| 29 | }; | ||
| 30 | 31 | ||
| 31 | static struct coreboot_table_header __iomem *ptr_header; | 32 | static struct coreboot_table_header __iomem *ptr_header; |
| 32 | 33 | ||
| 33 | /* | 34 | static int coreboot_bus_match(struct device *dev, struct device_driver *drv) |
| 34 | * This function parses the coreboot table for an entry that contains the base | ||
| 35 | * address of the given entry tag. The coreboot table consists of a header | ||
| 36 | * directly followed by a number of small, variable-sized entries, which each | ||
| 37 | * contain an identifying tag and their length as the first two fields. | ||
| 38 | */ | ||
| 39 | int coreboot_table_find(int tag, void *data, size_t data_size) | ||
| 40 | { | 35 | { |
| 41 | struct coreboot_table_header header; | 36 | struct coreboot_device *device = CB_DEV(dev); |
| 42 | struct coreboot_table_entry entry; | 37 | struct coreboot_driver *driver = CB_DRV(drv); |
| 43 | void *ptr_entry; | ||
| 44 | int i; | ||
| 45 | 38 | ||
| 46 | if (!ptr_header) | 39 | return device->entry.tag == driver->tag; |
| 47 | return -EPROBE_DEFER; | 40 | } |
| 48 | 41 | ||
| 42 | static int coreboot_bus_probe(struct device *dev) | ||
| 43 | { | ||
| 44 | int ret = -ENODEV; | ||
| 45 | struct coreboot_device *device = CB_DEV(dev); | ||
| 46 | struct coreboot_driver *driver = CB_DRV(dev->driver); | ||
| 47 | |||
| 48 | if (driver->probe) | ||
| 49 | ret = driver->probe(device); | ||
| 50 | |||
| 51 | return ret; | ||
| 52 | } | ||
| 53 | |||
| 54 | static int coreboot_bus_remove(struct device *dev) | ||
| 55 | { | ||
| 56 | int ret = 0; | ||
| 57 | struct coreboot_device *device = CB_DEV(dev); | ||
| 58 | struct coreboot_driver *driver = CB_DRV(dev->driver); | ||
| 59 | |||
| 60 | if (driver->remove) | ||
| 61 | ret = driver->remove(device); | ||
| 62 | |||
| 63 | return ret; | ||
| 64 | } | ||
| 65 | |||
| 66 | static struct bus_type coreboot_bus_type = { | ||
| 67 | .name = "coreboot", | ||
| 68 | .match = coreboot_bus_match, | ||
| 69 | .probe = coreboot_bus_probe, | ||
| 70 | .remove = coreboot_bus_remove, | ||
| 71 | }; | ||
| 72 | |||
| 73 | static int __init coreboot_bus_init(void) | ||
| 74 | { | ||
| 75 | return bus_register(&coreboot_bus_type); | ||
| 76 | } | ||
| 77 | module_init(coreboot_bus_init); | ||
| 78 | |||
| 79 | static void coreboot_device_release(struct device *dev) | ||
| 80 | { | ||
| 81 | struct coreboot_device *device = CB_DEV(dev); | ||
| 82 | |||
| 83 | kfree(device); | ||
| 84 | } | ||
| 85 | |||
| 86 | int coreboot_driver_register(struct coreboot_driver *driver) | ||
| 87 | { | ||
| 88 | driver->drv.bus = &coreboot_bus_type; | ||
| 89 | |||
| 90 | return driver_register(&driver->drv); | ||
| 91 | } | ||
| 92 | EXPORT_SYMBOL(coreboot_driver_register); | ||
| 93 | |||
| 94 | void coreboot_driver_unregister(struct coreboot_driver *driver) | ||
| 95 | { | ||
| 96 | driver_unregister(&driver->drv); | ||
| 97 | } | ||
| 98 | EXPORT_SYMBOL(coreboot_driver_unregister); | ||
| 99 | |||
| 100 | int coreboot_table_init(struct device *dev, void __iomem *ptr) | ||
| 101 | { | ||
| 102 | int i, ret; | ||
| 103 | void *ptr_entry; | ||
| 104 | struct coreboot_device *device; | ||
| 105 | struct coreboot_table_entry entry; | ||
| 106 | struct coreboot_table_header header; | ||
| 107 | |||
| 108 | ptr_header = ptr; | ||
| 49 | memcpy_fromio(&header, ptr_header, sizeof(header)); | 109 | memcpy_fromio(&header, ptr_header, sizeof(header)); |
| 50 | 110 | ||
| 51 | if (strncmp(header.signature, "LBIO", sizeof(header.signature))) { | 111 | if (strncmp(header.signature, "LBIO", sizeof(header.signature))) { |
| @@ -54,37 +114,41 @@ int coreboot_table_find(int tag, void *data, size_t data_size) | |||
| 54 | } | 114 | } |
| 55 | 115 | ||
| 56 | ptr_entry = (void *)ptr_header + header.header_bytes; | 116 | ptr_entry = (void *)ptr_header + header.header_bytes; |
| 57 | |||
| 58 | for (i = 0; i < header.table_entries; i++) { | 117 | for (i = 0; i < header.table_entries; i++) { |
| 59 | memcpy_fromio(&entry, ptr_entry, sizeof(entry)); | 118 | memcpy_fromio(&entry, ptr_entry, sizeof(entry)); |
| 60 | if (entry.tag == tag) { | ||
| 61 | if (data_size < entry.size) | ||
| 62 | return -EINVAL; | ||
| 63 | 119 | ||
| 64 | memcpy_fromio(data, ptr_entry, entry.size); | 120 | device = kzalloc(sizeof(struct device) + entry.size, GFP_KERNEL); |
| 121 | if (!device) { | ||
| 122 | ret = -ENOMEM; | ||
| 123 | break; | ||
| 124 | } | ||
| 125 | |||
| 126 | dev_set_name(&device->dev, "coreboot%d", i); | ||
| 127 | device->dev.parent = dev; | ||
| 128 | device->dev.bus = &coreboot_bus_type; | ||
| 129 | device->dev.release = coreboot_device_release; | ||
| 130 | memcpy_fromio(&device->entry, ptr_entry, entry.size); | ||
| 65 | 131 | ||
| 66 | return 0; | 132 | ret = device_register(&device->dev); |
| 133 | if (ret) { | ||
| 134 | put_device(&device->dev); | ||
| 135 | break; | ||
| 67 | } | 136 | } |
| 68 | 137 | ||
| 69 | ptr_entry += entry.size; | 138 | ptr_entry += entry.size; |
| 70 | } | 139 | } |
| 71 | 140 | ||
| 72 | return -ENOENT; | 141 | return ret; |
| 73 | } | ||
| 74 | EXPORT_SYMBOL(coreboot_table_find); | ||
| 75 | |||
| 76 | int coreboot_table_init(void __iomem *ptr) | ||
| 77 | { | ||
| 78 | ptr_header = ptr; | ||
| 79 | |||
| 80 | return 0; | ||
| 81 | } | 142 | } |
| 82 | EXPORT_SYMBOL(coreboot_table_init); | 143 | EXPORT_SYMBOL(coreboot_table_init); |
| 83 | 144 | ||
| 84 | int coreboot_table_exit(void) | 145 | int coreboot_table_exit(void) |
| 85 | { | 146 | { |
| 86 | if (ptr_header) | 147 | if (ptr_header) { |
| 148 | bus_unregister(&coreboot_bus_type); | ||
| 87 | iounmap(ptr_header); | 149 | iounmap(ptr_header); |
| 150 | ptr_header = NULL; | ||
| 151 | } | ||
| 88 | 152 | ||
| 89 | return 0; | 153 | return 0; |
| 90 | } | 154 | } |
diff --git a/drivers/firmware/google/coreboot_table.h b/drivers/firmware/google/coreboot_table.h index 6eff1ae0c5d3..8ad95a94481b 100644 --- a/drivers/firmware/google/coreboot_table.h +++ b/drivers/firmware/google/coreboot_table.h | |||
| @@ -3,7 +3,9 @@ | |||
| 3 | * | 3 | * |
| 4 | * Internal header for coreboot table access. | 4 | * Internal header for coreboot table access. |
| 5 | * | 5 | * |
| 6 | * Copyright 2014 Gerd Hoffmann <kraxel@redhat.com> | ||
| 6 | * Copyright 2017 Google Inc. | 7 | * Copyright 2017 Google Inc. |
| 8 | * Copyright 2017 Samuel Holland <samuel@sholland.org> | ||
| 7 | * | 9 | * |
| 8 | * This program is free software; you can redistribute it and/or modify | 10 | * This program is free software; you can redistribute it and/or modify |
| 9 | * it under the terms of the GNU General Public License v2.0 as published by | 11 | * it under the terms of the GNU General Public License v2.0 as published by |
| @@ -20,14 +22,6 @@ | |||
| 20 | 22 | ||
| 21 | #include <linux/io.h> | 23 | #include <linux/io.h> |
| 22 | 24 | ||
| 23 | /* List of coreboot entry structures that is used */ | ||
| 24 | struct lb_cbmem_ref { | ||
| 25 | uint32_t tag; | ||
| 26 | uint32_t size; | ||
| 27 | |||
| 28 | uint64_t cbmem_addr; | ||
| 29 | }; | ||
| 30 | |||
| 31 | /* Coreboot table header structure */ | 25 | /* Coreboot table header structure */ |
| 32 | struct coreboot_table_header { | 26 | struct coreboot_table_header { |
| 33 | char signature[4]; | 27 | char signature[4]; |
| @@ -38,11 +32,67 @@ struct coreboot_table_header { | |||
| 38 | u32 table_entries; | 32 | u32 table_entries; |
| 39 | }; | 33 | }; |
| 40 | 34 | ||
| 41 | /* Retrieve coreboot table entry with tag *tag* and copy it to data */ | 35 | /* List of coreboot entry structures that is used */ |
| 42 | int coreboot_table_find(int tag, void *data, size_t data_size); | 36 | /* Generic */ |
| 37 | struct coreboot_table_entry { | ||
| 38 | u32 tag; | ||
| 39 | u32 size; | ||
| 40 | }; | ||
| 41 | |||
| 42 | /* Points to a CBMEM entry */ | ||
| 43 | struct lb_cbmem_ref { | ||
| 44 | u32 tag; | ||
| 45 | u32 size; | ||
| 46 | |||
| 47 | u64 cbmem_addr; | ||
| 48 | }; | ||
| 49 | |||
| 50 | /* Describes framebuffer setup by coreboot */ | ||
| 51 | struct lb_framebuffer { | ||
| 52 | u32 tag; | ||
| 53 | u32 size; | ||
| 54 | |||
| 55 | u64 physical_address; | ||
| 56 | u32 x_resolution; | ||
| 57 | u32 y_resolution; | ||
| 58 | u32 bytes_per_line; | ||
| 59 | u8 bits_per_pixel; | ||
| 60 | u8 red_mask_pos; | ||
| 61 | u8 red_mask_size; | ||
| 62 | u8 green_mask_pos; | ||
| 63 | u8 green_mask_size; | ||
| 64 | u8 blue_mask_pos; | ||
| 65 | u8 blue_mask_size; | ||
| 66 | u8 reserved_mask_pos; | ||
| 67 | u8 reserved_mask_size; | ||
| 68 | }; | ||
| 69 | |||
| 70 | /* A device, additionally with information from coreboot. */ | ||
| 71 | struct coreboot_device { | ||
| 72 | struct device dev; | ||
| 73 | union { | ||
| 74 | struct coreboot_table_entry entry; | ||
| 75 | struct lb_cbmem_ref cbmem_ref; | ||
| 76 | struct lb_framebuffer framebuffer; | ||
| 77 | }; | ||
| 78 | }; | ||
| 79 | |||
| 80 | /* A driver for handling devices described in coreboot tables. */ | ||
| 81 | struct coreboot_driver { | ||
| 82 | int (*probe)(struct coreboot_device *); | ||
| 83 | int (*remove)(struct coreboot_device *); | ||
| 84 | struct device_driver drv; | ||
| 85 | u32 tag; | ||
| 86 | }; | ||
| 87 | |||
| 88 | /* Register a driver that uses the data from a coreboot table. */ | ||
| 89 | int coreboot_driver_register(struct coreboot_driver *driver); | ||
| 90 | |||
| 91 | /* Unregister a driver that uses the data from a coreboot table. */ | ||
| 92 | void coreboot_driver_unregister(struct coreboot_driver *driver); | ||
| 43 | 93 | ||
| 44 | /* Initialize coreboot table module given a pointer to iomem */ | 94 | /* Initialize coreboot table module given a pointer to iomem */ |
| 45 | int coreboot_table_init(void __iomem *ptr); | 95 | int coreboot_table_init(struct device *dev, void __iomem *ptr); |
| 46 | 96 | ||
| 47 | /* Cleanup coreboot table module */ | 97 | /* Cleanup coreboot table module */ |
| 48 | int coreboot_table_exit(void); | 98 | int coreboot_table_exit(void); |
diff --git a/drivers/firmware/google/framebuffer-coreboot.c b/drivers/firmware/google/framebuffer-coreboot.c new file mode 100644 index 000000000000..b8b49c067157 --- /dev/null +++ b/drivers/firmware/google/framebuffer-coreboot.c | |||
| @@ -0,0 +1,115 @@ | |||
| 1 | /* | ||
| 2 | * framebuffer-coreboot.c | ||
| 3 | * | ||
| 4 | * Memory based framebuffer accessed through coreboot table. | ||
| 5 | * | ||
| 6 | * Copyright 2012-2013 David Herrmann <dh.herrmann@gmail.com> | ||
| 7 | * Copyright 2017 Google Inc. | ||
| 8 | * Copyright 2017 Samuel Holland <samuel@sholland.org> | ||
| 9 | * | ||
| 10 | * This program is free software; you can redistribute it and/or modify | ||
| 11 | * it under the terms of the GNU General Public License v2.0 as published by | ||
| 12 | * the Free Software Foundation. | ||
| 13 | * | ||
| 14 | * This program is distributed in the hope that it will be useful, | ||
| 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 17 | * GNU General Public License for more details. | ||
| 18 | */ | ||
| 19 | |||
| 20 | #include <linux/device.h> | ||
| 21 | #include <linux/kernel.h> | ||
| 22 | #include <linux/mm.h> | ||
| 23 | #include <linux/module.h> | ||
| 24 | #include <linux/platform_data/simplefb.h> | ||
| 25 | #include <linux/platform_device.h> | ||
| 26 | |||
| 27 | #include "coreboot_table.h" | ||
| 28 | |||
| 29 | #define CB_TAG_FRAMEBUFFER 0x12 | ||
| 30 | |||
| 31 | static const struct simplefb_format formats[] = SIMPLEFB_FORMATS; | ||
| 32 | |||
| 33 | static int framebuffer_probe(struct coreboot_device *dev) | ||
| 34 | { | ||
| 35 | int i; | ||
| 36 | u32 length; | ||
| 37 | struct lb_framebuffer *fb = &dev->framebuffer; | ||
| 38 | struct platform_device *pdev; | ||
| 39 | struct resource res; | ||
| 40 | struct simplefb_platform_data pdata = { | ||
| 41 | .width = fb->x_resolution, | ||
| 42 | .height = fb->y_resolution, | ||
| 43 | .stride = fb->bytes_per_line, | ||
| 44 | .format = NULL, | ||
| 45 | }; | ||
| 46 | |||
| 47 | for (i = 0; i < ARRAY_SIZE(formats); ++i) { | ||
| 48 | if (fb->bits_per_pixel == formats[i].bits_per_pixel && | ||
| 49 | fb->red_mask_pos == formats[i].red.offset && | ||
| 50 | fb->red_mask_size == formats[i].red.length && | ||
| 51 | fb->green_mask_pos == formats[i].green.offset && | ||
| 52 | fb->green_mask_size == formats[i].green.length && | ||
| 53 | fb->blue_mask_pos == formats[i].blue.offset && | ||
| 54 | fb->blue_mask_size == formats[i].blue.length && | ||
| 55 | fb->reserved_mask_pos == formats[i].transp.offset && | ||
| 56 | fb->reserved_mask_size == formats[i].transp.length) | ||
| 57 | pdata.format = formats[i].name; | ||
| 58 | } | ||
| 59 | if (!pdata.format) | ||
| 60 | return -ENODEV; | ||
| 61 | |||
| 62 | memset(&res, 0, sizeof(res)); | ||
| 63 | res.flags = IORESOURCE_MEM | IORESOURCE_BUSY; | ||
| 64 | res.name = "Coreboot Framebuffer"; | ||
| 65 | res.start = fb->physical_address; | ||
| 66 | length = PAGE_ALIGN(fb->y_resolution * fb->bytes_per_line); | ||
| 67 | res.end = res.start + length - 1; | ||
| 68 | if (res.end <= res.start) | ||
| 69 | return -EINVAL; | ||
| 70 | |||
| 71 | pdev = platform_device_register_resndata(&dev->dev, | ||
| 72 | "simple-framebuffer", 0, | ||
| 73 | &res, 1, &pdata, | ||
| 74 | sizeof(pdata)); | ||
| 75 | if (IS_ERR(pdev)) | ||
| 76 | pr_warn("coreboot: could not register framebuffer\n"); | ||
| 77 | else | ||
| 78 | dev_set_drvdata(&dev->dev, pdev); | ||
| 79 | |||
| 80 | return PTR_ERR_OR_ZERO(pdev); | ||
| 81 | } | ||
| 82 | |||
| 83 | static int framebuffer_remove(struct coreboot_device *dev) | ||
| 84 | { | ||
| 85 | struct platform_device *pdev = dev_get_drvdata(&dev->dev); | ||
| 86 | |||
| 87 | platform_device_unregister(pdev); | ||
| 88 | |||
| 89 | return 0; | ||
| 90 | } | ||
| 91 | |||
| 92 | static struct coreboot_driver framebuffer_driver = { | ||
| 93 | .probe = framebuffer_probe, | ||
| 94 | .remove = framebuffer_remove, | ||
| 95 | .drv = { | ||
| 96 | .name = "framebuffer", | ||
| 97 | }, | ||
| 98 | .tag = CB_TAG_FRAMEBUFFER, | ||
| 99 | }; | ||
| 100 | |||
| 101 | static int __init coreboot_framebuffer_init(void) | ||
| 102 | { | ||
| 103 | return coreboot_driver_register(&framebuffer_driver); | ||
| 104 | } | ||
| 105 | |||
| 106 | static void coreboot_framebuffer_exit(void) | ||
| 107 | { | ||
| 108 | coreboot_driver_unregister(&framebuffer_driver); | ||
| 109 | } | ||
| 110 | |||
| 111 | module_init(coreboot_framebuffer_init); | ||
| 112 | module_exit(coreboot_framebuffer_exit); | ||
| 113 | |||
| 114 | MODULE_AUTHOR("Samuel Holland <samuel@sholland.org>"); | ||
| 115 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/firmware/google/memconsole-coreboot.c b/drivers/firmware/google/memconsole-coreboot.c index 52738887735c..b29e10757bfb 100644 --- a/drivers/firmware/google/memconsole-coreboot.c +++ b/drivers/firmware/google/memconsole-coreboot.c | |||
| @@ -15,9 +15,9 @@ | |||
| 15 | * GNU General Public License for more details. | 15 | * GNU General Public License for more details. |
| 16 | */ | 16 | */ |
| 17 | 17 | ||
| 18 | #include <linux/device.h> | ||
| 18 | #include <linux/kernel.h> | 19 | #include <linux/kernel.h> |
| 19 | #include <linux/module.h> | 20 | #include <linux/module.h> |
| 20 | #include <linux/platform_device.h> | ||
| 21 | 21 | ||
| 22 | #include "memconsole.h" | 22 | #include "memconsole.h" |
| 23 | #include "coreboot_table.h" | 23 | #include "coreboot_table.h" |
| @@ -73,18 +73,19 @@ static ssize_t memconsole_coreboot_read(char *buf, loff_t pos, size_t count) | |||
| 73 | return done; | 73 | return done; |
| 74 | } | 74 | } |
| 75 | 75 | ||
| 76 | static int memconsole_coreboot_init(phys_addr_t physaddr) | 76 | static int memconsole_probe(struct coreboot_device *dev) |
| 77 | { | 77 | { |
| 78 | struct cbmem_cons __iomem *tmp_cbmc; | 78 | struct cbmem_cons __iomem *tmp_cbmc; |
| 79 | 79 | ||
| 80 | tmp_cbmc = memremap(physaddr, sizeof(*tmp_cbmc), MEMREMAP_WB); | 80 | tmp_cbmc = memremap(dev->cbmem_ref.cbmem_addr, |
| 81 | sizeof(*tmp_cbmc), MEMREMAP_WB); | ||
| 81 | 82 | ||
| 82 | if (!tmp_cbmc) | 83 | if (!tmp_cbmc) |
| 83 | return -ENOMEM; | 84 | return -ENOMEM; |
| 84 | 85 | ||
| 85 | /* Read size only once to prevent overrun attack through /dev/mem. */ | 86 | /* Read size only once to prevent overrun attack through /dev/mem. */ |
| 86 | cbmem_console_size = tmp_cbmc->size_dont_access_after_boot; | 87 | cbmem_console_size = tmp_cbmc->size_dont_access_after_boot; |
| 87 | cbmem_console = memremap(physaddr, | 88 | cbmem_console = memremap(dev->cbmem_ref.cbmem_addr, |
| 88 | cbmem_console_size + sizeof(*cbmem_console), | 89 | cbmem_console_size + sizeof(*cbmem_console), |
| 89 | MEMREMAP_WB); | 90 | MEMREMAP_WB); |
| 90 | memunmap(tmp_cbmc); | 91 | memunmap(tmp_cbmc); |
| @@ -93,26 +94,11 @@ static int memconsole_coreboot_init(phys_addr_t physaddr) | |||
| 93 | return -ENOMEM; | 94 | return -ENOMEM; |
| 94 | 95 | ||
| 95 | memconsole_setup(memconsole_coreboot_read); | 96 | memconsole_setup(memconsole_coreboot_read); |
| 96 | return 0; | ||
| 97 | } | ||
| 98 | |||
| 99 | static int memconsole_probe(struct platform_device *pdev) | ||
| 100 | { | ||
| 101 | int ret; | ||
| 102 | struct lb_cbmem_ref entry; | ||
| 103 | |||
| 104 | ret = coreboot_table_find(CB_TAG_CBMEM_CONSOLE, &entry, sizeof(entry)); | ||
| 105 | if (ret) | ||
| 106 | return ret; | ||
| 107 | |||
| 108 | ret = memconsole_coreboot_init(entry.cbmem_addr); | ||
| 109 | if (ret) | ||
| 110 | return ret; | ||
| 111 | 97 | ||
| 112 | return memconsole_sysfs_init(); | 98 | return memconsole_sysfs_init(); |
| 113 | } | 99 | } |
| 114 | 100 | ||
| 115 | static int memconsole_remove(struct platform_device *pdev) | 101 | static int memconsole_remove(struct coreboot_device *dev) |
| 116 | { | 102 | { |
| 117 | memconsole_exit(); | 103 | memconsole_exit(); |
| 118 | 104 | ||
| @@ -122,28 +108,27 @@ static int memconsole_remove(struct platform_device *pdev) | |||
| 122 | return 0; | 108 | return 0; |
| 123 | } | 109 | } |
| 124 | 110 | ||
| 125 | static struct platform_driver memconsole_driver = { | 111 | static struct coreboot_driver memconsole_driver = { |
| 126 | .probe = memconsole_probe, | 112 | .probe = memconsole_probe, |
| 127 | .remove = memconsole_remove, | 113 | .remove = memconsole_remove, |
| 128 | .driver = { | 114 | .drv = { |
| 129 | .name = "memconsole", | 115 | .name = "memconsole", |
| 130 | }, | 116 | }, |
| 117 | .tag = CB_TAG_CBMEM_CONSOLE, | ||
| 131 | }; | 118 | }; |
| 132 | 119 | ||
| 133 | static int __init platform_memconsole_init(void) | 120 | static void coreboot_memconsole_exit(void) |
| 134 | { | 121 | { |
| 135 | struct platform_device *pdev; | 122 | coreboot_driver_unregister(&memconsole_driver); |
| 136 | 123 | } | |
| 137 | pdev = platform_device_register_simple("memconsole", -1, NULL, 0); | ||
| 138 | if (IS_ERR(pdev)) | ||
| 139 | return PTR_ERR(pdev); | ||
| 140 | |||
| 141 | platform_driver_register(&memconsole_driver); | ||
| 142 | 124 | ||
| 143 | return 0; | 125 | static int __init coreboot_memconsole_init(void) |
| 126 | { | ||
| 127 | return coreboot_driver_register(&memconsole_driver); | ||
| 144 | } | 128 | } |
| 145 | 129 | ||
| 146 | module_init(platform_memconsole_init); | 130 | module_exit(coreboot_memconsole_exit); |
| 131 | module_init(coreboot_memconsole_init); | ||
| 147 | 132 | ||
| 148 | MODULE_AUTHOR("Google, Inc."); | 133 | MODULE_AUTHOR("Google, Inc."); |
| 149 | MODULE_LICENSE("GPL"); | 134 | MODULE_LICENSE("GPL"); |
diff --git a/drivers/firmware/google/vpd.c b/drivers/firmware/google/vpd.c index e4b40f2b4627..e9db895916c3 100644 --- a/drivers/firmware/google/vpd.c +++ b/drivers/firmware/google/vpd.c | |||
| @@ -286,20 +286,15 @@ static int vpd_sections_init(phys_addr_t physaddr) | |||
| 286 | return 0; | 286 | return 0; |
| 287 | } | 287 | } |
| 288 | 288 | ||
| 289 | static int vpd_probe(struct platform_device *pdev) | 289 | static int vpd_probe(struct coreboot_device *dev) |
| 290 | { | 290 | { |
| 291 | int ret; | 291 | int ret; |
| 292 | struct lb_cbmem_ref entry; | ||
| 293 | |||
| 294 | ret = coreboot_table_find(CB_TAG_VPD, &entry, sizeof(entry)); | ||
| 295 | if (ret) | ||
| 296 | return ret; | ||
| 297 | 292 | ||
| 298 | vpd_kobj = kobject_create_and_add("vpd", firmware_kobj); | 293 | vpd_kobj = kobject_create_and_add("vpd", firmware_kobj); |
| 299 | if (!vpd_kobj) | 294 | if (!vpd_kobj) |
| 300 | return -ENOMEM; | 295 | return -ENOMEM; |
| 301 | 296 | ||
| 302 | ret = vpd_sections_init(entry.cbmem_addr); | 297 | ret = vpd_sections_init(dev->cbmem_ref.cbmem_addr); |
| 303 | if (ret) { | 298 | if (ret) { |
| 304 | kobject_put(vpd_kobj); | 299 | kobject_put(vpd_kobj); |
| 305 | return ret; | 300 | return ret; |
| @@ -308,7 +303,7 @@ static int vpd_probe(struct platform_device *pdev) | |||
| 308 | return 0; | 303 | return 0; |
| 309 | } | 304 | } |
| 310 | 305 | ||
| 311 | static int vpd_remove(struct platform_device *pdev) | 306 | static int vpd_remove(struct coreboot_device *dev) |
| 312 | { | 307 | { |
| 313 | vpd_section_destroy(&ro_vpd); | 308 | vpd_section_destroy(&ro_vpd); |
| 314 | vpd_section_destroy(&rw_vpd); | 309 | vpd_section_destroy(&rw_vpd); |
| @@ -318,41 +313,27 @@ static int vpd_remove(struct platform_device *pdev) | |||
| 318 | return 0; | 313 | return 0; |
| 319 | } | 314 | } |
| 320 | 315 | ||
| 321 | static struct platform_driver vpd_driver = { | 316 | static struct coreboot_driver vpd_driver = { |
| 322 | .probe = vpd_probe, | 317 | .probe = vpd_probe, |
| 323 | .remove = vpd_remove, | 318 | .remove = vpd_remove, |
| 324 | .driver = { | 319 | .drv = { |
| 325 | .name = "vpd", | 320 | .name = "vpd", |
| 326 | }, | 321 | }, |
| 322 | .tag = CB_TAG_VPD, | ||
| 327 | }; | 323 | }; |
| 328 | 324 | ||
| 329 | static struct platform_device *vpd_pdev; | 325 | static int __init coreboot_vpd_init(void) |
| 330 | |||
| 331 | static int __init vpd_platform_init(void) | ||
| 332 | { | 326 | { |
| 333 | int ret; | 327 | return coreboot_driver_register(&vpd_driver); |
| 334 | |||
| 335 | ret = platform_driver_register(&vpd_driver); | ||
| 336 | if (ret) | ||
| 337 | return ret; | ||
| 338 | |||
| 339 | vpd_pdev = platform_device_register_simple("vpd", -1, NULL, 0); | ||
| 340 | if (IS_ERR(vpd_pdev)) { | ||
| 341 | platform_driver_unregister(&vpd_driver); | ||
| 342 | return PTR_ERR(vpd_pdev); | ||
| 343 | } | ||
| 344 | |||
| 345 | return 0; | ||
| 346 | } | 328 | } |
| 347 | 329 | ||
| 348 | static void __exit vpd_platform_exit(void) | 330 | static void __exit coreboot_vpd_exit(void) |
| 349 | { | 331 | { |
| 350 | platform_device_unregister(vpd_pdev); | 332 | coreboot_driver_unregister(&vpd_driver); |
| 351 | platform_driver_unregister(&vpd_driver); | ||
| 352 | } | 333 | } |
| 353 | 334 | ||
| 354 | module_init(vpd_platform_init); | 335 | module_init(coreboot_vpd_init); |
| 355 | module_exit(vpd_platform_exit); | 336 | module_exit(coreboot_vpd_exit); |
| 356 | 337 | ||
| 357 | MODULE_AUTHOR("Google, Inc."); | 338 | MODULE_AUTHOR("Google, Inc."); |
| 358 | MODULE_LICENSE("GPL"); | 339 | MODULE_LICENSE("GPL"); |
diff --git a/drivers/fpga/Kconfig b/drivers/fpga/Kconfig index f47ef848bcd0..ee9c5420c47f 100644 --- a/drivers/fpga/Kconfig +++ b/drivers/fpga/Kconfig | |||
| @@ -53,7 +53,6 @@ config FPGA_MGR_ALTERA_CVP | |||
| 53 | config FPGA_MGR_ZYNQ_FPGA | 53 | config FPGA_MGR_ZYNQ_FPGA |
| 54 | tristate "Xilinx Zynq FPGA" | 54 | tristate "Xilinx Zynq FPGA" |
| 55 | depends on ARCH_ZYNQ || COMPILE_TEST | 55 | depends on ARCH_ZYNQ || COMPILE_TEST |
| 56 | depends on HAS_DMA | ||
| 57 | help | 56 | help |
| 58 | FPGA manager driver support for Xilinx Zynq FPGAs. | 57 | FPGA manager driver support for Xilinx Zynq FPGAs. |
| 59 | 58 | ||
| @@ -70,6 +69,13 @@ config FPGA_MGR_ICE40_SPI | |||
| 70 | help | 69 | help |
| 71 | FPGA manager driver support for Lattice iCE40 FPGAs over SPI. | 70 | FPGA manager driver support for Lattice iCE40 FPGAs over SPI. |
| 72 | 71 | ||
| 72 | config FPGA_MGR_MACHXO2_SPI | ||
| 73 | tristate "Lattice MachXO2 SPI" | ||
| 74 | depends on SPI | ||
| 75 | help | ||
| 76 | FPGA manager driver support for Lattice MachXO2 configuration | ||
| 77 | over slave SPI interface. | ||
| 78 | |||
| 73 | config FPGA_MGR_TS73XX | 79 | config FPGA_MGR_TS73XX |
| 74 | tristate "Technologic Systems TS-73xx SBC FPGA Manager" | 80 | tristate "Technologic Systems TS-73xx SBC FPGA Manager" |
| 75 | depends on ARCH_EP93XX && MACH_TS72XX | 81 | depends on ARCH_EP93XX && MACH_TS72XX |
diff --git a/drivers/fpga/Makefile b/drivers/fpga/Makefile index 3cb276a0f88d..f9803dad6919 100644 --- a/drivers/fpga/Makefile +++ b/drivers/fpga/Makefile | |||
| @@ -10,6 +10,7 @@ obj-$(CONFIG_FPGA) += fpga-mgr.o | |||
| 10 | obj-$(CONFIG_FPGA_MGR_ALTERA_CVP) += altera-cvp.o | 10 | obj-$(CONFIG_FPGA_MGR_ALTERA_CVP) += altera-cvp.o |
| 11 | obj-$(CONFIG_FPGA_MGR_ALTERA_PS_SPI) += altera-ps-spi.o | 11 | obj-$(CONFIG_FPGA_MGR_ALTERA_PS_SPI) += altera-ps-spi.o |
| 12 | obj-$(CONFIG_FPGA_MGR_ICE40_SPI) += ice40-spi.o | 12 | obj-$(CONFIG_FPGA_MGR_ICE40_SPI) += ice40-spi.o |
| 13 | obj-$(CONFIG_FPGA_MGR_MACHXO2_SPI) += machxo2-spi.o | ||
| 13 | obj-$(CONFIG_FPGA_MGR_SOCFPGA) += socfpga.o | 14 | obj-$(CONFIG_FPGA_MGR_SOCFPGA) += socfpga.o |
| 14 | obj-$(CONFIG_FPGA_MGR_SOCFPGA_A10) += socfpga-a10.o | 15 | obj-$(CONFIG_FPGA_MGR_SOCFPGA_A10) += socfpga-a10.o |
| 15 | obj-$(CONFIG_FPGA_MGR_TS73XX) += ts73xx-fpga.o | 16 | obj-$(CONFIG_FPGA_MGR_TS73XX) += ts73xx-fpga.o |
diff --git a/drivers/fpga/altera-cvp.c b/drivers/fpga/altera-cvp.c index 77b04e4b3254..dd4edd8f22ce 100644 --- a/drivers/fpga/altera-cvp.c +++ b/drivers/fpga/altera-cvp.c | |||
| @@ -401,6 +401,7 @@ static int altera_cvp_probe(struct pci_dev *pdev, | |||
| 401 | const struct pci_device_id *dev_id) | 401 | const struct pci_device_id *dev_id) |
| 402 | { | 402 | { |
| 403 | struct altera_cvp_conf *conf; | 403 | struct altera_cvp_conf *conf; |
| 404 | struct fpga_manager *mgr; | ||
| 404 | u16 cmd, val; | 405 | u16 cmd, val; |
| 405 | int ret; | 406 | int ret; |
| 406 | 407 | ||
| @@ -452,16 +453,24 @@ static int altera_cvp_probe(struct pci_dev *pdev, | |||
| 452 | snprintf(conf->mgr_name, sizeof(conf->mgr_name), "%s @%s", | 453 | snprintf(conf->mgr_name, sizeof(conf->mgr_name), "%s @%s", |
| 453 | ALTERA_CVP_MGR_NAME, pci_name(pdev)); | 454 | ALTERA_CVP_MGR_NAME, pci_name(pdev)); |
| 454 | 455 | ||
| 455 | ret = fpga_mgr_register(&pdev->dev, conf->mgr_name, | 456 | mgr = fpga_mgr_create(&pdev->dev, conf->mgr_name, |
| 456 | &altera_cvp_ops, conf); | 457 | &altera_cvp_ops, conf); |
| 457 | if (ret) | 458 | if (!mgr) |
| 459 | return -ENOMEM; | ||
| 460 | |||
| 461 | pci_set_drvdata(pdev, mgr); | ||
| 462 | |||
| 463 | ret = fpga_mgr_register(mgr); | ||
| 464 | if (ret) { | ||
| 465 | fpga_mgr_free(mgr); | ||
| 458 | goto err_unmap; | 466 | goto err_unmap; |
| 467 | } | ||
| 459 | 468 | ||
| 460 | ret = driver_create_file(&altera_cvp_driver.driver, | 469 | ret = driver_create_file(&altera_cvp_driver.driver, |
| 461 | &driver_attr_chkcfg); | 470 | &driver_attr_chkcfg); |
| 462 | if (ret) { | 471 | if (ret) { |
| 463 | dev_err(&pdev->dev, "Can't create sysfs chkcfg file\n"); | 472 | dev_err(&pdev->dev, "Can't create sysfs chkcfg file\n"); |
| 464 | fpga_mgr_unregister(&pdev->dev); | 473 | fpga_mgr_unregister(mgr); |
| 465 | goto err_unmap; | 474 | goto err_unmap; |
| 466 | } | 475 | } |
| 467 | 476 | ||
| @@ -483,7 +492,7 @@ static void altera_cvp_remove(struct pci_dev *pdev) | |||
| 483 | u16 cmd; | 492 | u16 cmd; |
| 484 | 493 | ||
| 485 | driver_remove_file(&altera_cvp_driver.driver, &driver_attr_chkcfg); | 494 | driver_remove_file(&altera_cvp_driver.driver, &driver_attr_chkcfg); |
| 486 | fpga_mgr_unregister(&pdev->dev); | 495 | fpga_mgr_unregister(mgr); |
| 487 | pci_iounmap(pdev, conf->map); | 496 | pci_iounmap(pdev, conf->map); |
| 488 | pci_release_region(pdev, CVP_BAR); | 497 | pci_release_region(pdev, CVP_BAR); |
| 489 | pci_read_config_word(pdev, PCI_COMMAND, &cmd); | 498 | pci_read_config_word(pdev, PCI_COMMAND, &cmd); |
diff --git a/drivers/fpga/altera-fpga2sdram.c b/drivers/fpga/altera-fpga2sdram.c index d4eeb74388da..23660ccd634b 100644 --- a/drivers/fpga/altera-fpga2sdram.c +++ b/drivers/fpga/altera-fpga2sdram.c | |||
| @@ -1,19 +1,8 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 1 | /* | 2 | /* |
| 2 | * FPGA to SDRAM Bridge Driver for Altera SoCFPGA Devices | 3 | * FPGA to SDRAM Bridge Driver for Altera SoCFPGA Devices |
| 3 | * | 4 | * |
| 4 | * Copyright (C) 2013-2016 Altera Corporation, All Rights Reserved. | 5 | * Copyright (C) 2013-2016 Altera Corporation, All Rights Reserved. |
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify it | ||
| 7 | * under the terms and conditions of the GNU General Public License, | ||
| 8 | * version 2, as published by the Free Software Foundation. | ||
| 9 | * | ||
| 10 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
| 11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 13 | * more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU General Public License along with | ||
| 16 | * this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 17 | */ | 6 | */ |
| 18 | 7 | ||
| 19 | /* | 8 | /* |
| @@ -106,6 +95,7 @@ static int alt_fpga_bridge_probe(struct platform_device *pdev) | |||
| 106 | { | 95 | { |
| 107 | struct device *dev = &pdev->dev; | 96 | struct device *dev = &pdev->dev; |
| 108 | struct alt_fpga2sdram_data *priv; | 97 | struct alt_fpga2sdram_data *priv; |
| 98 | struct fpga_bridge *br; | ||
| 109 | u32 enable; | 99 | u32 enable; |
| 110 | struct regmap *sysmgr; | 100 | struct regmap *sysmgr; |
| 111 | int ret = 0; | 101 | int ret = 0; |
| @@ -131,10 +121,18 @@ static int alt_fpga_bridge_probe(struct platform_device *pdev) | |||
| 131 | /* Get f2s bridge configuration saved in handoff register */ | 121 | /* Get f2s bridge configuration saved in handoff register */ |
| 132 | regmap_read(sysmgr, SYSMGR_ISWGRP_HANDOFF3, &priv->mask); | 122 | regmap_read(sysmgr, SYSMGR_ISWGRP_HANDOFF3, &priv->mask); |
| 133 | 123 | ||
| 134 | ret = fpga_bridge_register(dev, F2S_BRIDGE_NAME, | 124 | br = fpga_bridge_create(dev, F2S_BRIDGE_NAME, |
| 135 | &altera_fpga2sdram_br_ops, priv); | 125 | &altera_fpga2sdram_br_ops, priv); |
| 136 | if (ret) | 126 | if (!br) |
| 127 | return -ENOMEM; | ||
| 128 | |||
| 129 | platform_set_drvdata(pdev, br); | ||
| 130 | |||
| 131 | ret = fpga_bridge_register(br); | ||
| 132 | if (ret) { | ||
| 133 | fpga_bridge_free(br); | ||
| 137 | return ret; | 134 | return ret; |
| 135 | } | ||
| 138 | 136 | ||
| 139 | dev_info(dev, "driver initialized with handoff %08x\n", priv->mask); | 137 | dev_info(dev, "driver initialized with handoff %08x\n", priv->mask); |
| 140 | 138 | ||
| @@ -146,7 +144,7 @@ static int alt_fpga_bridge_probe(struct platform_device *pdev) | |||
| 146 | (enable ? "enabling" : "disabling")); | 144 | (enable ? "enabling" : "disabling")); |
| 147 | ret = _alt_fpga2sdram_enable_set(priv, enable); | 145 | ret = _alt_fpga2sdram_enable_set(priv, enable); |
| 148 | if (ret) { | 146 | if (ret) { |
| 149 | fpga_bridge_unregister(&pdev->dev); | 147 | fpga_bridge_unregister(br); |
| 150 | return ret; | 148 | return ret; |
| 151 | } | 149 | } |
| 152 | } | 150 | } |
| @@ -157,7 +155,9 @@ static int alt_fpga_bridge_probe(struct platform_device *pdev) | |||
| 157 | 155 | ||
| 158 | static int alt_fpga_bridge_remove(struct platform_device *pdev) | 156 | static int alt_fpga_bridge_remove(struct platform_device *pdev) |
| 159 | { | 157 | { |
| 160 | fpga_bridge_unregister(&pdev->dev); | 158 | struct fpga_bridge *br = platform_get_drvdata(pdev); |
| 159 | |||
| 160 | fpga_bridge_unregister(br); | ||
| 161 | 161 | ||
| 162 | return 0; | 162 | return 0; |
| 163 | } | 163 | } |
diff --git a/drivers/fpga/altera-freeze-bridge.c b/drivers/fpga/altera-freeze-bridge.c index 6159cfcf78a2..ffd586c48ecf 100644 --- a/drivers/fpga/altera-freeze-bridge.c +++ b/drivers/fpga/altera-freeze-bridge.c | |||
| @@ -1,19 +1,8 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 1 | /* | 2 | /* |
| 2 | * FPGA Freeze Bridge Controller | 3 | * FPGA Freeze Bridge Controller |
| 3 | * | 4 | * |
| 4 | * Copyright (C) 2016 Altera Corporation. All rights reserved. | 5 | * Copyright (C) 2016 Altera Corporation. All rights reserved. |
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify it | ||
| 7 | * under the terms and conditions of the GNU General Public License, | ||
| 8 | * version 2, as published by the Free Software Foundation. | ||
| 9 | * | ||
| 10 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
| 11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 13 | * more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU General Public License along with | ||
| 16 | * this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 17 | */ | 6 | */ |
| 18 | #include <linux/delay.h> | 7 | #include <linux/delay.h> |
| 19 | #include <linux/io.h> | 8 | #include <linux/io.h> |
| @@ -221,8 +210,10 @@ static int altera_freeze_br_probe(struct platform_device *pdev) | |||
| 221 | struct device_node *np = pdev->dev.of_node; | 210 | struct device_node *np = pdev->dev.of_node; |
| 222 | void __iomem *base_addr; | 211 | void __iomem *base_addr; |
| 223 | struct altera_freeze_br_data *priv; | 212 | struct altera_freeze_br_data *priv; |
| 213 | struct fpga_bridge *br; | ||
| 224 | struct resource *res; | 214 | struct resource *res; |
| 225 | u32 status, revision; | 215 | u32 status, revision; |
| 216 | int ret; | ||
| 226 | 217 | ||
| 227 | if (!np) | 218 | if (!np) |
| 228 | return -ENODEV; | 219 | return -ENODEV; |
| @@ -254,13 +245,27 @@ static int altera_freeze_br_probe(struct platform_device *pdev) | |||
| 254 | 245 | ||
| 255 | priv->base_addr = base_addr; | 246 | priv->base_addr = base_addr; |
| 256 | 247 | ||
| 257 | return fpga_bridge_register(dev, FREEZE_BRIDGE_NAME, | 248 | br = fpga_bridge_create(dev, FREEZE_BRIDGE_NAME, |
| 258 | &altera_freeze_br_br_ops, priv); | 249 | &altera_freeze_br_br_ops, priv); |
| 250 | if (!br) | ||
| 251 | return -ENOMEM; | ||
| 252 | |||
| 253 | platform_set_drvdata(pdev, br); | ||
| 254 | |||
| 255 | ret = fpga_bridge_register(br); | ||
| 256 | if (ret) { | ||
| 257 | fpga_bridge_free(br); | ||
| 258 | return ret; | ||
| 259 | } | ||
| 260 | |||
| 261 | return 0; | ||
| 259 | } | 262 | } |
| 260 | 263 | ||
| 261 | static int altera_freeze_br_remove(struct platform_device *pdev) | 264 | static int altera_freeze_br_remove(struct platform_device *pdev) |
| 262 | { | 265 | { |
| 263 | fpga_bridge_unregister(&pdev->dev); | 266 | struct fpga_bridge *br = platform_get_drvdata(pdev); |
| 267 | |||
| 268 | fpga_bridge_unregister(br); | ||
| 264 | 269 | ||
| 265 | return 0; | 270 | return 0; |
| 266 | } | 271 | } |
diff --git a/drivers/fpga/altera-hps2fpga.c b/drivers/fpga/altera-hps2fpga.c index 406d2f10741f..a974d3f60321 100644 --- a/drivers/fpga/altera-hps2fpga.c +++ b/drivers/fpga/altera-hps2fpga.c | |||
| @@ -1,3 +1,4 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 1 | /* | 2 | /* |
| 2 | * FPGA to/from HPS Bridge Driver for Altera SoCFPGA Devices | 3 | * FPGA to/from HPS Bridge Driver for Altera SoCFPGA Devices |
| 3 | * | 4 | * |
| @@ -6,18 +7,6 @@ | |||
| 6 | * Includes this patch from the mailing list: | 7 | * Includes this patch from the mailing list: |
| 7 | * fpga: altera-hps2fpga: fix HPS2FPGA bridge visibility to L3 masters | 8 | * fpga: altera-hps2fpga: fix HPS2FPGA bridge visibility to L3 masters |
| 8 | * Signed-off-by: Anatolij Gustschin <agust@denx.de> | 9 | * Signed-off-by: Anatolij Gustschin <agust@denx.de> |
| 9 | * | ||
| 10 | * This program is free software; you can redistribute it and/or modify it | ||
| 11 | * under the terms and conditions of the GNU General Public License, | ||
| 12 | * version 2, as published by the Free Software Foundation. | ||
| 13 | * | ||
| 14 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
| 15 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 16 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 17 | * more details. | ||
| 18 | * | ||
| 19 | * You should have received a copy of the GNU General Public License along with | ||
| 20 | * this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 21 | */ | 10 | */ |
| 22 | 11 | ||
| 23 | /* | 12 | /* |
| @@ -139,6 +128,7 @@ static int alt_fpga_bridge_probe(struct platform_device *pdev) | |||
| 139 | struct device *dev = &pdev->dev; | 128 | struct device *dev = &pdev->dev; |
| 140 | struct altera_hps2fpga_data *priv; | 129 | struct altera_hps2fpga_data *priv; |
| 141 | const struct of_device_id *of_id; | 130 | const struct of_device_id *of_id; |
| 131 | struct fpga_bridge *br; | ||
| 142 | u32 enable; | 132 | u32 enable; |
| 143 | int ret; | 133 | int ret; |
| 144 | 134 | ||
| @@ -190,11 +180,24 @@ static int alt_fpga_bridge_probe(struct platform_device *pdev) | |||
| 190 | } | 180 | } |
| 191 | } | 181 | } |
| 192 | 182 | ||
| 193 | ret = fpga_bridge_register(dev, priv->name, &altera_hps2fpga_br_ops, | 183 | br = fpga_bridge_create(dev, priv->name, &altera_hps2fpga_br_ops, priv); |
| 194 | priv); | 184 | if (!br) { |
| 195 | err: | 185 | ret = -ENOMEM; |
| 186 | goto err; | ||
| 187 | } | ||
| 188 | |||
| 189 | platform_set_drvdata(pdev, br); | ||
| 190 | |||
| 191 | ret = fpga_bridge_register(br); | ||
| 196 | if (ret) | 192 | if (ret) |
| 197 | clk_disable_unprepare(priv->clk); | 193 | goto err_free; |
| 194 | |||
| 195 | return 0; | ||
| 196 | |||
| 197 | err_free: | ||
| 198 | fpga_bridge_free(br); | ||
| 199 | err: | ||
| 200 | clk_disable_unprepare(priv->clk); | ||
| 198 | 201 | ||
| 199 | return ret; | 202 | return ret; |
| 200 | } | 203 | } |
| @@ -204,7 +207,7 @@ static int alt_fpga_bridge_remove(struct platform_device *pdev) | |||
| 204 | struct fpga_bridge *bridge = platform_get_drvdata(pdev); | 207 | struct fpga_bridge *bridge = platform_get_drvdata(pdev); |
| 205 | struct altera_hps2fpga_data *priv = bridge->priv; | 208 | struct altera_hps2fpga_data *priv = bridge->priv; |
| 206 | 209 | ||
| 207 | fpga_bridge_unregister(&pdev->dev); | 210 | fpga_bridge_unregister(bridge); |
| 208 | 211 | ||
| 209 | clk_disable_unprepare(priv->clk); | 212 | clk_disable_unprepare(priv->clk); |
| 210 | 213 | ||
diff --git a/drivers/fpga/altera-pr-ip-core-plat.c b/drivers/fpga/altera-pr-ip-core-plat.c index 8fb36b8b4648..b293d83143f1 100644 --- a/drivers/fpga/altera-pr-ip-core-plat.c +++ b/drivers/fpga/altera-pr-ip-core-plat.c | |||
| @@ -1,3 +1,4 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 1 | /* | 2 | /* |
| 2 | * Driver for Altera Partial Reconfiguration IP Core | 3 | * Driver for Altera Partial Reconfiguration IP Core |
| 3 | * | 4 | * |
| @@ -5,18 +6,6 @@ | |||
| 5 | * | 6 | * |
| 6 | * Based on socfpga-a10.c Copyright (C) 2015-2016 Altera Corporation | 7 | * Based on socfpga-a10.c Copyright (C) 2015-2016 Altera Corporation |
| 7 | * by Alan Tull <atull@opensource.altera.com> | 8 | * by Alan Tull <atull@opensource.altera.com> |
| 8 | * | ||
| 9 | * This program is free software; you can redistribute it and/or modify it | ||
| 10 | * under the terms and conditions of the GNU General Public License, | ||
| 11 | * version 2, as published by the Free Software Foundation. | ||
| 12 | * | ||
| 13 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
| 14 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 15 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 16 | * more details. | ||
| 17 | * | ||
| 18 | * You should have received a copy of the GNU General Public License along with | ||
| 19 | * this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 20 | */ | 9 | */ |
| 21 | #include <linux/fpga/altera-pr-ip-core.h> | 10 | #include <linux/fpga/altera-pr-ip-core.h> |
| 22 | #include <linux/module.h> | 11 | #include <linux/module.h> |
diff --git a/drivers/fpga/altera-pr-ip-core.c b/drivers/fpga/altera-pr-ip-core.c index a7b31f9797ce..65e0b6a2c031 100644 --- a/drivers/fpga/altera-pr-ip-core.c +++ b/drivers/fpga/altera-pr-ip-core.c | |||
| @@ -1,3 +1,4 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 1 | /* | 2 | /* |
| 2 | * Driver for Altera Partial Reconfiguration IP Core | 3 | * Driver for Altera Partial Reconfiguration IP Core |
| 3 | * | 4 | * |
| @@ -5,18 +6,6 @@ | |||
| 5 | * | 6 | * |
| 6 | * Based on socfpga-a10.c Copyright (C) 2015-2016 Altera Corporation | 7 | * Based on socfpga-a10.c Copyright (C) 2015-2016 Altera Corporation |
| 7 | * by Alan Tull <atull@opensource.altera.com> | 8 | * by Alan Tull <atull@opensource.altera.com> |
| 8 | * | ||
| 9 | * This program is free software; you can redistribute it and/or modify it | ||
| 10 | * under the terms and conditions of the GNU General Public License, | ||
| 11 | * version 2, as published by the Free Software Foundation. | ||
| 12 | * | ||
| 13 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
| 14 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 15 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 16 | * more details. | ||
| 17 | * | ||
| 18 | * You should have received a copy of the GNU General Public License along with | ||
| 19 | * this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 20 | */ | 9 | */ |
| 21 | #include <linux/delay.h> | 10 | #include <linux/delay.h> |
| 22 | #include <linux/fpga/altera-pr-ip-core.h> | 11 | #include <linux/fpga/altera-pr-ip-core.h> |
| @@ -187,6 +176,8 @@ static const struct fpga_manager_ops alt_pr_ops = { | |||
| 187 | int alt_pr_register(struct device *dev, void __iomem *reg_base) | 176 | int alt_pr_register(struct device *dev, void __iomem *reg_base) |
| 188 | { | 177 | { |
| 189 | struct alt_pr_priv *priv; | 178 | struct alt_pr_priv *priv; |
| 179 | struct fpga_manager *mgr; | ||
| 180 | int ret; | ||
| 190 | u32 val; | 181 | u32 val; |
| 191 | 182 | ||
| 192 | priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); | 183 | priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); |
| @@ -201,15 +192,27 @@ int alt_pr_register(struct device *dev, void __iomem *reg_base) | |||
| 201 | (val & ALT_PR_CSR_STATUS_MSK) >> ALT_PR_CSR_STATUS_SFT, | 192 | (val & ALT_PR_CSR_STATUS_MSK) >> ALT_PR_CSR_STATUS_SFT, |
| 202 | (int)(val & ALT_PR_CSR_PR_START)); | 193 | (int)(val & ALT_PR_CSR_PR_START)); |
| 203 | 194 | ||
| 204 | return fpga_mgr_register(dev, dev_name(dev), &alt_pr_ops, priv); | 195 | mgr = fpga_mgr_create(dev, dev_name(dev), &alt_pr_ops, priv); |
| 196 | if (!mgr) | ||
| 197 | return -ENOMEM; | ||
| 198 | |||
| 199 | dev_set_drvdata(dev, mgr); | ||
| 200 | |||
| 201 | ret = fpga_mgr_register(mgr); | ||
| 202 | if (ret) | ||
| 203 | fpga_mgr_free(mgr); | ||
| 204 | |||
| 205 | return ret; | ||
| 205 | } | 206 | } |
| 206 | EXPORT_SYMBOL_GPL(alt_pr_register); | 207 | EXPORT_SYMBOL_GPL(alt_pr_register); |
| 207 | 208 | ||
| 208 | int alt_pr_unregister(struct device *dev) | 209 | int alt_pr_unregister(struct device *dev) |
| 209 | { | 210 | { |
| 211 | struct fpga_manager *mgr = dev_get_drvdata(dev); | ||
| 212 | |||
| 210 | dev_dbg(dev, "%s\n", __func__); | 213 | dev_dbg(dev, "%s\n", __func__); |
| 211 | 214 | ||
| 212 | fpga_mgr_unregister(dev); | 215 | fpga_mgr_unregister(mgr); |
| 213 | 216 | ||
| 214 | return 0; | 217 | return 0; |
| 215 | } | 218 | } |
diff --git a/drivers/fpga/altera-ps-spi.c b/drivers/fpga/altera-ps-spi.c index 06d212a3d49d..24b25c626036 100644 --- a/drivers/fpga/altera-ps-spi.c +++ b/drivers/fpga/altera-ps-spi.c | |||
| @@ -238,6 +238,8 @@ static int altera_ps_probe(struct spi_device *spi) | |||
| 238 | { | 238 | { |
| 239 | struct altera_ps_conf *conf; | 239 | struct altera_ps_conf *conf; |
| 240 | const struct of_device_id *of_id; | 240 | const struct of_device_id *of_id; |
| 241 | struct fpga_manager *mgr; | ||
| 242 | int ret; | ||
| 241 | 243 | ||
| 242 | conf = devm_kzalloc(&spi->dev, sizeof(*conf), GFP_KERNEL); | 244 | conf = devm_kzalloc(&spi->dev, sizeof(*conf), GFP_KERNEL); |
| 243 | if (!conf) | 245 | if (!conf) |
| @@ -273,13 +275,25 @@ static int altera_ps_probe(struct spi_device *spi) | |||
| 273 | snprintf(conf->mgr_name, sizeof(conf->mgr_name), "%s %s", | 275 | snprintf(conf->mgr_name, sizeof(conf->mgr_name), "%s %s", |
| 274 | dev_driver_string(&spi->dev), dev_name(&spi->dev)); | 276 | dev_driver_string(&spi->dev), dev_name(&spi->dev)); |
| 275 | 277 | ||
| 276 | return fpga_mgr_register(&spi->dev, conf->mgr_name, | 278 | mgr = fpga_mgr_create(&spi->dev, conf->mgr_name, |
| 277 | &altera_ps_ops, conf); | 279 | &altera_ps_ops, conf); |
| 280 | if (!mgr) | ||
| 281 | return -ENOMEM; | ||
| 282 | |||
| 283 | spi_set_drvdata(spi, mgr); | ||
| 284 | |||
| 285 | ret = fpga_mgr_register(mgr); | ||
| 286 | if (ret) | ||
| 287 | fpga_mgr_free(mgr); | ||
| 288 | |||
| 289 | return ret; | ||
| 278 | } | 290 | } |
| 279 | 291 | ||
| 280 | static int altera_ps_remove(struct spi_device *spi) | 292 | static int altera_ps_remove(struct spi_device *spi) |
| 281 | { | 293 | { |
| 282 | fpga_mgr_unregister(&spi->dev); | 294 | struct fpga_manager *mgr = spi_get_drvdata(spi); |
| 295 | |||
| 296 | fpga_mgr_unregister(mgr); | ||
| 283 | 297 | ||
| 284 | return 0; | 298 | return 0; |
| 285 | } | 299 | } |
diff --git a/drivers/fpga/fpga-bridge.c b/drivers/fpga/fpga-bridge.c index 31bd2c59c305..24b8f98b73ec 100644 --- a/drivers/fpga/fpga-bridge.c +++ b/drivers/fpga/fpga-bridge.c | |||
| @@ -1,20 +1,9 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 1 | /* | 2 | /* |
| 2 | * FPGA Bridge Framework Driver | 3 | * FPGA Bridge Framework Driver |
| 3 | * | 4 | * |
| 4 | * Copyright (C) 2013-2016 Altera Corporation, All Rights Reserved. | 5 | * Copyright (C) 2013-2016 Altera Corporation, All Rights Reserved. |
| 5 | * Copyright (C) 2017 Intel Corporation | 6 | * Copyright (C) 2017 Intel Corporation |
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or modify it | ||
| 8 | * under the terms and conditions of the GNU General Public License, | ||
| 9 | * version 2, as published by the Free Software Foundation. | ||
| 10 | * | ||
| 11 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 14 | * more details. | ||
| 15 | * | ||
| 16 | * You should have received a copy of the GNU General Public License along with | ||
| 17 | * this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 18 | */ | 7 | */ |
| 19 | #include <linux/fpga/fpga-bridge.h> | 8 | #include <linux/fpga/fpga-bridge.h> |
| 20 | #include <linux/idr.h> | 9 | #include <linux/idr.h> |
| @@ -132,6 +121,7 @@ static int fpga_bridge_dev_match(struct device *dev, const void *data) | |||
| 132 | /** | 121 | /** |
| 133 | * fpga_bridge_get - get an exclusive reference to a fpga bridge | 122 | * fpga_bridge_get - get an exclusive reference to a fpga bridge |
| 134 | * @dev: parent device that fpga bridge was registered with | 123 | * @dev: parent device that fpga bridge was registered with |
| 124 | * @info: fpga manager info | ||
| 135 | * | 125 | * |
| 136 | * Given a device, get an exclusive reference to a fpga bridge. | 126 | * Given a device, get an exclusive reference to a fpga bridge. |
| 137 | * | 127 | * |
| @@ -328,28 +318,29 @@ static struct attribute *fpga_bridge_attrs[] = { | |||
| 328 | ATTRIBUTE_GROUPS(fpga_bridge); | 318 | ATTRIBUTE_GROUPS(fpga_bridge); |
| 329 | 319 | ||
| 330 | /** | 320 | /** |
| 331 | * fpga_bridge_register - register a fpga bridge driver | 321 | * fpga_bridge_create - create and initialize a struct fpga_bridge |
| 332 | * @dev: FPGA bridge device from pdev | 322 | * @dev: FPGA bridge device from pdev |
| 333 | * @name: FPGA bridge name | 323 | * @name: FPGA bridge name |
| 334 | * @br_ops: pointer to structure of fpga bridge ops | 324 | * @br_ops: pointer to structure of fpga bridge ops |
| 335 | * @priv: FPGA bridge private data | 325 | * @priv: FPGA bridge private data |
| 336 | * | 326 | * |
| 337 | * Return: 0 for success, error code otherwise. | 327 | * Return: struct fpga_bridge or NULL |
| 338 | */ | 328 | */ |
| 339 | int fpga_bridge_register(struct device *dev, const char *name, | 329 | struct fpga_bridge *fpga_bridge_create(struct device *dev, const char *name, |
| 340 | const struct fpga_bridge_ops *br_ops, void *priv) | 330 | const struct fpga_bridge_ops *br_ops, |
| 331 | void *priv) | ||
| 341 | { | 332 | { |
| 342 | struct fpga_bridge *bridge; | 333 | struct fpga_bridge *bridge; |
| 343 | int id, ret = 0; | 334 | int id, ret = 0; |
| 344 | 335 | ||
| 345 | if (!name || !strlen(name)) { | 336 | if (!name || !strlen(name)) { |
| 346 | dev_err(dev, "Attempt to register with no name!\n"); | 337 | dev_err(dev, "Attempt to register with no name!\n"); |
| 347 | return -EINVAL; | 338 | return NULL; |
| 348 | } | 339 | } |
| 349 | 340 | ||
| 350 | bridge = kzalloc(sizeof(*bridge), GFP_KERNEL); | 341 | bridge = kzalloc(sizeof(*bridge), GFP_KERNEL); |
| 351 | if (!bridge) | 342 | if (!bridge) |
| 352 | return -ENOMEM; | 343 | return NULL; |
| 353 | 344 | ||
| 354 | id = ida_simple_get(&fpga_bridge_ida, 0, 0, GFP_KERNEL); | 345 | id = ida_simple_get(&fpga_bridge_ida, 0, 0, GFP_KERNEL); |
| 355 | if (id < 0) { | 346 | if (id < 0) { |
| @@ -370,40 +361,62 @@ int fpga_bridge_register(struct device *dev, const char *name, | |||
| 370 | bridge->dev.parent = dev; | 361 | bridge->dev.parent = dev; |
| 371 | bridge->dev.of_node = dev->of_node; | 362 | bridge->dev.of_node = dev->of_node; |
| 372 | bridge->dev.id = id; | 363 | bridge->dev.id = id; |
| 373 | dev_set_drvdata(dev, bridge); | ||
| 374 | 364 | ||
| 375 | ret = dev_set_name(&bridge->dev, "br%d", id); | 365 | ret = dev_set_name(&bridge->dev, "br%d", id); |
| 376 | if (ret) | 366 | if (ret) |
| 377 | goto error_device; | 367 | goto error_device; |
| 378 | 368 | ||
| 379 | ret = device_add(&bridge->dev); | 369 | return bridge; |
| 380 | if (ret) | ||
| 381 | goto error_device; | ||
| 382 | |||
| 383 | of_platform_populate(dev->of_node, NULL, NULL, dev); | ||
| 384 | |||
| 385 | dev_info(bridge->dev.parent, "fpga bridge [%s] registered\n", | ||
| 386 | bridge->name); | ||
| 387 | |||
| 388 | return 0; | ||
| 389 | 370 | ||
| 390 | error_device: | 371 | error_device: |
| 391 | ida_simple_remove(&fpga_bridge_ida, id); | 372 | ida_simple_remove(&fpga_bridge_ida, id); |
| 392 | error_kfree: | 373 | error_kfree: |
| 393 | kfree(bridge); | 374 | kfree(bridge); |
| 394 | 375 | ||
| 395 | return ret; | 376 | return NULL; |
| 396 | } | 377 | } |
| 397 | EXPORT_SYMBOL_GPL(fpga_bridge_register); | 378 | EXPORT_SYMBOL_GPL(fpga_bridge_create); |
| 398 | 379 | ||
| 399 | /** | 380 | /** |
| 400 | * fpga_bridge_unregister - unregister a fpga bridge driver | 381 | * fpga_bridge_free - free a fpga bridge and its id |
| 401 | * @dev: FPGA bridge device from pdev | 382 | * @bridge: FPGA bridge struct created by fpga_bridge_create |
| 402 | */ | 383 | */ |
| 403 | void fpga_bridge_unregister(struct device *dev) | 384 | void fpga_bridge_free(struct fpga_bridge *bridge) |
| 404 | { | 385 | { |
| 405 | struct fpga_bridge *bridge = dev_get_drvdata(dev); | 386 | ida_simple_remove(&fpga_bridge_ida, bridge->dev.id); |
| 387 | kfree(bridge); | ||
| 388 | } | ||
| 389 | EXPORT_SYMBOL_GPL(fpga_bridge_free); | ||
| 406 | 390 | ||
| 391 | /** | ||
| 392 | * fpga_bridge_register - register a fpga bridge | ||
| 393 | * @bridge: FPGA bridge struct created by fpga_bridge_create | ||
| 394 | * | ||
| 395 | * Return: 0 for success, error code otherwise. | ||
| 396 | */ | ||
| 397 | int fpga_bridge_register(struct fpga_bridge *bridge) | ||
| 398 | { | ||
| 399 | struct device *dev = &bridge->dev; | ||
| 400 | int ret; | ||
| 401 | |||
| 402 | ret = device_add(dev); | ||
| 403 | if (ret) | ||
| 404 | return ret; | ||
| 405 | |||
| 406 | of_platform_populate(dev->of_node, NULL, NULL, dev); | ||
| 407 | |||
| 408 | dev_info(dev->parent, "fpga bridge [%s] registered\n", bridge->name); | ||
| 409 | |||
| 410 | return 0; | ||
| 411 | } | ||
| 412 | EXPORT_SYMBOL_GPL(fpga_bridge_register); | ||
| 413 | |||
| 414 | /** | ||
| 415 | * fpga_bridge_unregister - unregister and free a fpga bridge | ||
| 416 | * @bridge: FPGA bridge struct created by fpga_bridge_create | ||
| 417 | */ | ||
| 418 | void fpga_bridge_unregister(struct fpga_bridge *bridge) | ||
| 419 | { | ||
| 407 | /* | 420 | /* |
| 408 | * If the low level driver provides a method for putting bridge into | 421 | * If the low level driver provides a method for putting bridge into |
| 409 | * a desired state upon unregister, do it. | 422 | * a desired state upon unregister, do it. |
| @@ -419,8 +432,7 @@ static void fpga_bridge_dev_release(struct device *dev) | |||
| 419 | { | 432 | { |
| 420 | struct fpga_bridge *bridge = to_fpga_bridge(dev); | 433 | struct fpga_bridge *bridge = to_fpga_bridge(dev); |
| 421 | 434 | ||
| 422 | ida_simple_remove(&fpga_bridge_ida, bridge->dev.id); | 435 | fpga_bridge_free(bridge); |
| 423 | kfree(bridge); | ||
| 424 | } | 436 | } |
| 425 | 437 | ||
| 426 | static int __init fpga_bridge_dev_init(void) | 438 | static int __init fpga_bridge_dev_init(void) |
diff --git a/drivers/fpga/fpga-mgr.c b/drivers/fpga/fpga-mgr.c index 9939d2cbc9a6..c1564cf827fe 100644 --- a/drivers/fpga/fpga-mgr.c +++ b/drivers/fpga/fpga-mgr.c | |||
| @@ -1,3 +1,4 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 1 | /* | 2 | /* |
| 2 | * FPGA Manager Core | 3 | * FPGA Manager Core |
| 3 | * | 4 | * |
| @@ -6,18 +7,6 @@ | |||
| 6 | * | 7 | * |
| 7 | * With code from the mailing list: | 8 | * With code from the mailing list: |
| 8 | * Copyright (C) 2013 Xilinx, Inc. | 9 | * Copyright (C) 2013 Xilinx, Inc. |
| 9 | * | ||
| 10 | * This program is free software; you can redistribute it and/or modify it | ||
| 11 | * under the terms and conditions of the GNU General Public License, | ||
| 12 | * version 2, as published by the Free Software Foundation. | ||
| 13 | * | ||
| 14 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
| 15 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 16 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 17 | * more details. | ||
| 18 | * | ||
| 19 | * You should have received a copy of the GNU General Public License along with | ||
| 20 | * this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 21 | */ | 10 | */ |
| 22 | #include <linux/firmware.h> | 11 | #include <linux/firmware.h> |
| 23 | #include <linux/fpga/fpga-mgr.h> | 12 | #include <linux/fpga/fpga-mgr.h> |
| @@ -32,6 +21,12 @@ | |||
| 32 | static DEFINE_IDA(fpga_mgr_ida); | 21 | static DEFINE_IDA(fpga_mgr_ida); |
| 33 | static struct class *fpga_mgr_class; | 22 | static struct class *fpga_mgr_class; |
| 34 | 23 | ||
| 24 | /** | ||
| 25 | * fpga_image_info_alloc - Allocate a FPGA image info struct | ||
| 26 | * @dev: owning device | ||
| 27 | * | ||
| 28 | * Return: struct fpga_image_info or NULL | ||
| 29 | */ | ||
| 35 | struct fpga_image_info *fpga_image_info_alloc(struct device *dev) | 30 | struct fpga_image_info *fpga_image_info_alloc(struct device *dev) |
| 36 | { | 31 | { |
| 37 | struct fpga_image_info *info; | 32 | struct fpga_image_info *info; |
| @@ -50,6 +45,10 @@ struct fpga_image_info *fpga_image_info_alloc(struct device *dev) | |||
| 50 | } | 45 | } |
| 51 | EXPORT_SYMBOL_GPL(fpga_image_info_alloc); | 46 | EXPORT_SYMBOL_GPL(fpga_image_info_alloc); |
| 52 | 47 | ||
| 48 | /** | ||
| 49 | * fpga_image_info_free - Free a FPGA image info struct | ||
| 50 | * @info: FPGA image info struct to free | ||
| 51 | */ | ||
| 53 | void fpga_image_info_free(struct fpga_image_info *info) | 52 | void fpga_image_info_free(struct fpga_image_info *info) |
| 54 | { | 53 | { |
| 55 | struct device *dev; | 54 | struct device *dev; |
| @@ -234,7 +233,7 @@ static int fpga_mgr_buf_load_mapped(struct fpga_manager *mgr, | |||
| 234 | /** | 233 | /** |
| 235 | * fpga_mgr_buf_load - load fpga from image in buffer | 234 | * fpga_mgr_buf_load - load fpga from image in buffer |
| 236 | * @mgr: fpga manager | 235 | * @mgr: fpga manager |
| 237 | * @flags: flags setting fpga confuration modes | 236 | * @info: fpga image info |
| 238 | * @buf: buffer contain fpga image | 237 | * @buf: buffer contain fpga image |
| 239 | * @count: byte count of buf | 238 | * @count: byte count of buf |
| 240 | * | 239 | * |
| @@ -343,6 +342,16 @@ static int fpga_mgr_firmware_load(struct fpga_manager *mgr, | |||
| 343 | return ret; | 342 | return ret; |
| 344 | } | 343 | } |
| 345 | 344 | ||
| 345 | /** | ||
| 346 | * fpga_mgr_load - load FPGA from scatter/gather table, buffer, or firmware | ||
| 347 | * @mgr: fpga manager | ||
| 348 | * @info: fpga image information. | ||
| 349 | * | ||
| 350 | * Load the FPGA from an image which is indicated in @info. If successful, the | ||
| 351 | * FPGA ends up in operating mode. | ||
| 352 | * | ||
| 353 | * Return: 0 on success, negative error code otherwise. | ||
| 354 | */ | ||
| 346 | int fpga_mgr_load(struct fpga_manager *mgr, struct fpga_image_info *info) | 355 | int fpga_mgr_load(struct fpga_manager *mgr, struct fpga_image_info *info) |
| 347 | { | 356 | { |
| 348 | if (info->sgt) | 357 | if (info->sgt) |
| @@ -429,11 +438,9 @@ static int fpga_mgr_dev_match(struct device *dev, const void *data) | |||
| 429 | } | 438 | } |
| 430 | 439 | ||
| 431 | /** | 440 | /** |
| 432 | * fpga_mgr_get - get a reference to a fpga mgr | 441 | * fpga_mgr_get - Given a device, get a reference to a fpga mgr. |
| 433 | * @dev: parent device that fpga mgr was registered with | 442 | * @dev: parent device that fpga mgr was registered with |
| 434 | * | 443 | * |
| 435 | * Given a device, get a reference to a fpga mgr. | ||
| 436 | * | ||
| 437 | * Return: fpga manager struct or IS_ERR() condition containing error code. | 444 | * Return: fpga manager struct or IS_ERR() condition containing error code. |
| 438 | */ | 445 | */ |
| 439 | struct fpga_manager *fpga_mgr_get(struct device *dev) | 446 | 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) | |||
| 453 | } | 460 | } |
| 454 | 461 | ||
| 455 | /** | 462 | /** |
| 456 | * of_fpga_mgr_get - get a reference to a fpga mgr | 463 | * of_fpga_mgr_get - Given a device node, get a reference to a fpga mgr. |
| 457 | * @node: device node | ||
| 458 | * | 464 | * |
| 459 | * Given a device node, get a reference to a fpga mgr. | 465 | * @node: device node |
| 460 | * | 466 | * |
| 461 | * Return: fpga manager struct or IS_ERR() condition containing error code. | 467 | * Return: fpga manager struct or IS_ERR() condition containing error code. |
| 462 | */ | 468 | */ |
| @@ -489,7 +495,10 @@ EXPORT_SYMBOL_GPL(fpga_mgr_put); | |||
| 489 | * @mgr: fpga manager | 495 | * @mgr: fpga manager |
| 490 | * | 496 | * |
| 491 | * Given a pointer to FPGA Manager (from fpga_mgr_get() or | 497 | * Given a pointer to FPGA Manager (from fpga_mgr_get() or |
| 492 | * of_fpga_mgr_put()) attempt to get the mutex. | 498 | * of_fpga_mgr_put()) attempt to get the mutex. The user should call |
| 499 | * fpga_mgr_lock() and verify that it returns 0 before attempting to | ||
| 500 | * program the FPGA. Likewise, the user should call fpga_mgr_unlock | ||
| 501 | * when done programming the FPGA. | ||
| 493 | * | 502 | * |
| 494 | * Return: 0 for success or -EBUSY | 503 | * Return: 0 for success or -EBUSY |
| 495 | */ | 504 | */ |
| @@ -505,7 +514,7 @@ int fpga_mgr_lock(struct fpga_manager *mgr) | |||
| 505 | EXPORT_SYMBOL_GPL(fpga_mgr_lock); | 514 | EXPORT_SYMBOL_GPL(fpga_mgr_lock); |
| 506 | 515 | ||
| 507 | /** | 516 | /** |
| 508 | * fpga_mgr_unlock - Unlock FPGA manager | 517 | * fpga_mgr_unlock - Unlock FPGA manager after done programming |
| 509 | * @mgr: fpga manager | 518 | * @mgr: fpga manager |
| 510 | */ | 519 | */ |
| 511 | void fpga_mgr_unlock(struct fpga_manager *mgr) | 520 | void fpga_mgr_unlock(struct fpga_manager *mgr) |
| @@ -515,17 +524,17 @@ void fpga_mgr_unlock(struct fpga_manager *mgr) | |||
| 515 | EXPORT_SYMBOL_GPL(fpga_mgr_unlock); | 524 | EXPORT_SYMBOL_GPL(fpga_mgr_unlock); |
| 516 | 525 | ||
| 517 | /** | 526 | /** |
| 518 | * fpga_mgr_register - register a low level fpga manager driver | 527 | * fpga_mgr_create - create and initialize a FPGA manager struct |
| 519 | * @dev: fpga manager device from pdev | 528 | * @dev: fpga manager device from pdev |
| 520 | * @name: fpga manager name | 529 | * @name: fpga manager name |
| 521 | * @mops: pointer to structure of fpga manager ops | 530 | * @mops: pointer to structure of fpga manager ops |
| 522 | * @priv: fpga manager private data | 531 | * @priv: fpga manager private data |
| 523 | * | 532 | * |
| 524 | * Return: 0 on success, negative error code otherwise. | 533 | * Return: pointer to struct fpga_manager or NULL |
| 525 | */ | 534 | */ |
| 526 | int fpga_mgr_register(struct device *dev, const char *name, | 535 | struct fpga_manager *fpga_mgr_create(struct device *dev, const char *name, |
| 527 | const struct fpga_manager_ops *mops, | 536 | const struct fpga_manager_ops *mops, |
| 528 | void *priv) | 537 | void *priv) |
| 529 | { | 538 | { |
| 530 | struct fpga_manager *mgr; | 539 | struct fpga_manager *mgr; |
| 531 | int id, ret; | 540 | int id, ret; |
| @@ -534,17 +543,17 @@ int fpga_mgr_register(struct device *dev, const char *name, | |||
| 534 | !mops->write_init || (!mops->write && !mops->write_sg) || | 543 | !mops->write_init || (!mops->write && !mops->write_sg) || |
| 535 | (mops->write && mops->write_sg)) { | 544 | (mops->write && mops->write_sg)) { |
| 536 | dev_err(dev, "Attempt to register without fpga_manager_ops\n"); | 545 | dev_err(dev, "Attempt to register without fpga_manager_ops\n"); |
| 537 | return -EINVAL; | 546 | return NULL; |
| 538 | } | 547 | } |
| 539 | 548 | ||
| 540 | if (!name || !strlen(name)) { | 549 | if (!name || !strlen(name)) { |
| 541 | dev_err(dev, "Attempt to register with no name!\n"); | 550 | dev_err(dev, "Attempt to register with no name!\n"); |
| 542 | return -EINVAL; | 551 | return NULL; |
| 543 | } | 552 | } |
| 544 | 553 | ||
| 545 | mgr = kzalloc(sizeof(*mgr), GFP_KERNEL); | 554 | mgr = kzalloc(sizeof(*mgr), GFP_KERNEL); |
| 546 | if (!mgr) | 555 | if (!mgr) |
| 547 | return -ENOMEM; | 556 | return NULL; |
| 548 | 557 | ||
| 549 | id = ida_simple_get(&fpga_mgr_ida, 0, 0, GFP_KERNEL); | 558 | id = ida_simple_get(&fpga_mgr_ida, 0, 0, GFP_KERNEL); |
| 550 | if (id < 0) { | 559 | if (id < 0) { |
| @@ -558,25 +567,56 @@ int fpga_mgr_register(struct device *dev, const char *name, | |||
| 558 | mgr->mops = mops; | 567 | mgr->mops = mops; |
| 559 | mgr->priv = priv; | 568 | mgr->priv = priv; |
| 560 | 569 | ||
| 561 | /* | ||
| 562 | * Initialize framework state by requesting low level driver read state | ||
| 563 | * from device. FPGA may be in reset mode or may have been programmed | ||
| 564 | * by bootloader or EEPROM. | ||
| 565 | */ | ||
| 566 | mgr->state = mgr->mops->state(mgr); | ||
| 567 | |||
| 568 | device_initialize(&mgr->dev); | 570 | device_initialize(&mgr->dev); |
| 569 | mgr->dev.class = fpga_mgr_class; | 571 | mgr->dev.class = fpga_mgr_class; |
| 570 | mgr->dev.groups = mops->groups; | 572 | mgr->dev.groups = mops->groups; |
| 571 | mgr->dev.parent = dev; | 573 | mgr->dev.parent = dev; |
| 572 | mgr->dev.of_node = dev->of_node; | 574 | mgr->dev.of_node = dev->of_node; |
| 573 | mgr->dev.id = id; | 575 | mgr->dev.id = id; |
| 574 | dev_set_drvdata(dev, mgr); | ||
| 575 | 576 | ||
| 576 | ret = dev_set_name(&mgr->dev, "fpga%d", id); | 577 | ret = dev_set_name(&mgr->dev, "fpga%d", id); |
| 577 | if (ret) | 578 | if (ret) |
| 578 | goto error_device; | 579 | goto error_device; |
| 579 | 580 | ||
| 581 | return mgr; | ||
| 582 | |||
| 583 | error_device: | ||
| 584 | ida_simple_remove(&fpga_mgr_ida, id); | ||
| 585 | error_kfree: | ||
| 586 | kfree(mgr); | ||
| 587 | |||
| 588 | return NULL; | ||
| 589 | } | ||
| 590 | EXPORT_SYMBOL_GPL(fpga_mgr_create); | ||
| 591 | |||
| 592 | /** | ||
| 593 | * fpga_mgr_free - deallocate a FPGA manager | ||
| 594 | * @mgr: fpga manager struct created by fpga_mgr_create | ||
| 595 | */ | ||
| 596 | void fpga_mgr_free(struct fpga_manager *mgr) | ||
| 597 | { | ||
| 598 | ida_simple_remove(&fpga_mgr_ida, mgr->dev.id); | ||
| 599 | kfree(mgr); | ||
| 600 | } | ||
| 601 | EXPORT_SYMBOL_GPL(fpga_mgr_free); | ||
| 602 | |||
| 603 | /** | ||
| 604 | * fpga_mgr_register - register a FPGA manager | ||
| 605 | * @mgr: fpga manager struct created by fpga_mgr_create | ||
| 606 | * | ||
| 607 | * Return: 0 on success, negative error code otherwise. | ||
| 608 | */ | ||
| 609 | int fpga_mgr_register(struct fpga_manager *mgr) | ||
| 610 | { | ||
| 611 | int ret; | ||
| 612 | |||
| 613 | /* | ||
| 614 | * Initialize framework state by requesting low level driver read state | ||
| 615 | * from device. FPGA may be in reset mode or may have been programmed | ||
| 616 | * by bootloader or EEPROM. | ||
| 617 | */ | ||
| 618 | mgr->state = mgr->mops->state(mgr); | ||
| 619 | |||
| 580 | ret = device_add(&mgr->dev); | 620 | ret = device_add(&mgr->dev); |
| 581 | if (ret) | 621 | if (ret) |
| 582 | goto error_device; | 622 | goto error_device; |
| @@ -586,22 +626,18 @@ int fpga_mgr_register(struct device *dev, const char *name, | |||
| 586 | return 0; | 626 | return 0; |
| 587 | 627 | ||
| 588 | error_device: | 628 | error_device: |
| 589 | ida_simple_remove(&fpga_mgr_ida, id); | 629 | ida_simple_remove(&fpga_mgr_ida, mgr->dev.id); |
| 590 | error_kfree: | ||
| 591 | kfree(mgr); | ||
| 592 | 630 | ||
| 593 | return ret; | 631 | return ret; |
| 594 | } | 632 | } |
| 595 | EXPORT_SYMBOL_GPL(fpga_mgr_register); | 633 | EXPORT_SYMBOL_GPL(fpga_mgr_register); |
| 596 | 634 | ||
| 597 | /** | 635 | /** |
| 598 | * fpga_mgr_unregister - unregister a low level fpga manager driver | 636 | * fpga_mgr_unregister - unregister and free a FPGA manager |
| 599 | * @dev: fpga manager device from pdev | 637 | * @mgr: fpga manager struct |
| 600 | */ | 638 | */ |
| 601 | void fpga_mgr_unregister(struct device *dev) | 639 | void fpga_mgr_unregister(struct fpga_manager *mgr) |
| 602 | { | 640 | { |
| 603 | struct fpga_manager *mgr = dev_get_drvdata(dev); | ||
| 604 | |||
| 605 | dev_info(&mgr->dev, "%s %s\n", __func__, mgr->name); | 641 | dev_info(&mgr->dev, "%s %s\n", __func__, mgr->name); |
| 606 | 642 | ||
| 607 | /* | 643 | /* |
| @@ -619,8 +655,7 @@ static void fpga_mgr_dev_release(struct device *dev) | |||
| 619 | { | 655 | { |
| 620 | struct fpga_manager *mgr = to_fpga_manager(dev); | 656 | struct fpga_manager *mgr = to_fpga_manager(dev); |
| 621 | 657 | ||
| 622 | ida_simple_remove(&fpga_mgr_ida, mgr->dev.id); | 658 | fpga_mgr_free(mgr); |
| 623 | kfree(mgr); | ||
| 624 | } | 659 | } |
| 625 | 660 | ||
| 626 | static int __init fpga_mgr_class_init(void) | 661 | static int __init fpga_mgr_class_init(void) |
diff --git a/drivers/fpga/fpga-region.c b/drivers/fpga/fpga-region.c index edab2a2e03ef..6d214d75c7be 100644 --- a/drivers/fpga/fpga-region.c +++ b/drivers/fpga/fpga-region.c | |||
| @@ -1,22 +1,10 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 1 | /* | 2 | /* |
| 2 | * FPGA Region - Device Tree support for FPGA programming under Linux | 3 | * FPGA Region - Device Tree support for FPGA programming under Linux |
| 3 | * | 4 | * |
| 4 | * Copyright (C) 2013-2016 Altera Corporation | 5 | * Copyright (C) 2013-2016 Altera Corporation |
| 5 | * Copyright (C) 2017 Intel Corporation | 6 | * Copyright (C) 2017 Intel Corporation |
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or modify it | ||
| 8 | * under the terms and conditions of the GNU General Public License, | ||
| 9 | * version 2, as published by the Free Software Foundation. | ||
| 10 | * | ||
| 11 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 14 | * more details. | ||
| 15 | * | ||
| 16 | * You should have received a copy of the GNU General Public License along with | ||
| 17 | * this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 18 | */ | 7 | */ |
| 19 | |||
| 20 | #include <linux/fpga/fpga-bridge.h> | 8 | #include <linux/fpga/fpga-bridge.h> |
| 21 | #include <linux/fpga/fpga-mgr.h> | 9 | #include <linux/fpga/fpga-mgr.h> |
| 22 | #include <linux/fpga/fpga-region.h> | 10 | #include <linux/fpga/fpga-region.h> |
| @@ -93,8 +81,16 @@ static void fpga_region_put(struct fpga_region *region) | |||
| 93 | 81 | ||
| 94 | /** | 82 | /** |
| 95 | * fpga_region_program_fpga - program FPGA | 83 | * fpga_region_program_fpga - program FPGA |
| 84 | * | ||
| 96 | * @region: FPGA region | 85 | * @region: FPGA region |
| 86 | * | ||
| 97 | * Program an FPGA using fpga image info (region->info). | 87 | * Program an FPGA using fpga image info (region->info). |
| 88 | * If the region has a get_bridges function, the exclusive reference for the | ||
| 89 | * bridges will be held if programming succeeds. This is intended to prevent | ||
| 90 | * reprogramming the region until the caller considers it safe to do so. | ||
| 91 | * The caller will need to call fpga_bridges_put() before attempting to | ||
| 92 | * reprogram the region. | ||
| 93 | * | ||
| 98 | * Return 0 for success or negative error code. | 94 | * Return 0 for success or negative error code. |
| 99 | */ | 95 | */ |
| 100 | int fpga_region_program_fpga(struct fpga_region *region) | 96 | int fpga_region_program_fpga(struct fpga_region *region) |
| @@ -162,45 +158,86 @@ err_put_region: | |||
| 162 | } | 158 | } |
| 163 | EXPORT_SYMBOL_GPL(fpga_region_program_fpga); | 159 | EXPORT_SYMBOL_GPL(fpga_region_program_fpga); |
| 164 | 160 | ||
| 165 | int fpga_region_register(struct device *dev, struct fpga_region *region) | 161 | /** |
| 162 | * fpga_region_create - alloc and init a struct fpga_region | ||
| 163 | * @dev: device parent | ||
| 164 | * @mgr: manager that programs this region | ||
| 165 | * @get_bridges: optional function to get bridges to a list | ||
| 166 | * | ||
| 167 | * Return: struct fpga_region or NULL | ||
| 168 | */ | ||
| 169 | struct fpga_region | ||
| 170 | *fpga_region_create(struct device *dev, | ||
| 171 | struct fpga_manager *mgr, | ||
| 172 | int (*get_bridges)(struct fpga_region *)) | ||
| 166 | { | 173 | { |
| 174 | struct fpga_region *region; | ||
| 167 | int id, ret = 0; | 175 | int id, ret = 0; |
| 168 | 176 | ||
| 177 | region = kzalloc(sizeof(*region), GFP_KERNEL); | ||
| 178 | if (!region) | ||
| 179 | return NULL; | ||
| 180 | |||
| 169 | id = ida_simple_get(&fpga_region_ida, 0, 0, GFP_KERNEL); | 181 | id = ida_simple_get(&fpga_region_ida, 0, 0, GFP_KERNEL); |
| 170 | if (id < 0) | 182 | if (id < 0) |
| 171 | return id; | 183 | goto err_free; |
| 172 | 184 | ||
| 185 | region->mgr = mgr; | ||
| 186 | region->get_bridges = get_bridges; | ||
| 173 | mutex_init(®ion->mutex); | 187 | mutex_init(®ion->mutex); |
| 174 | INIT_LIST_HEAD(®ion->bridge_list); | 188 | INIT_LIST_HEAD(®ion->bridge_list); |
| 189 | |||
| 175 | device_initialize(®ion->dev); | 190 | device_initialize(®ion->dev); |
| 176 | region->dev.groups = region->groups; | ||
| 177 | region->dev.class = fpga_region_class; | 191 | region->dev.class = fpga_region_class; |
| 178 | region->dev.parent = dev; | 192 | region->dev.parent = dev; |
| 179 | region->dev.of_node = dev->of_node; | 193 | region->dev.of_node = dev->of_node; |
| 180 | region->dev.id = id; | 194 | region->dev.id = id; |
| 181 | dev_set_drvdata(dev, region); | ||
| 182 | 195 | ||
| 183 | ret = dev_set_name(®ion->dev, "region%d", id); | 196 | ret = dev_set_name(®ion->dev, "region%d", id); |
| 184 | if (ret) | 197 | if (ret) |
| 185 | goto err_remove; | 198 | goto err_remove; |
| 186 | 199 | ||
| 187 | ret = device_add(®ion->dev); | 200 | return region; |
| 188 | if (ret) | ||
| 189 | goto err_remove; | ||
| 190 | |||
| 191 | return 0; | ||
| 192 | 201 | ||
| 193 | err_remove: | 202 | err_remove: |
| 194 | ida_simple_remove(&fpga_region_ida, id); | 203 | ida_simple_remove(&fpga_region_ida, id); |
| 195 | return ret; | 204 | err_free: |
| 205 | kfree(region); | ||
| 206 | |||
| 207 | return NULL; | ||
| 208 | } | ||
| 209 | EXPORT_SYMBOL_GPL(fpga_region_create); | ||
| 210 | |||
| 211 | /** | ||
| 212 | * fpga_region_free - free a struct fpga_region | ||
| 213 | * @region: FPGA region created by fpga_region_create | ||
| 214 | */ | ||
| 215 | void fpga_region_free(struct fpga_region *region) | ||
| 216 | { | ||
| 217 | ida_simple_remove(&fpga_region_ida, region->dev.id); | ||
| 218 | kfree(region); | ||
| 219 | } | ||
| 220 | EXPORT_SYMBOL_GPL(fpga_region_free); | ||
| 221 | |||
| 222 | /** | ||
| 223 | * fpga_region_register - register a FPGA region | ||
| 224 | * @region: FPGA region created by fpga_region_create | ||
| 225 | * Return: 0 or -errno | ||
| 226 | */ | ||
| 227 | int fpga_region_register(struct fpga_region *region) | ||
| 228 | { | ||
| 229 | return device_add(®ion->dev); | ||
| 230 | |||
| 196 | } | 231 | } |
| 197 | EXPORT_SYMBOL_GPL(fpga_region_register); | 232 | EXPORT_SYMBOL_GPL(fpga_region_register); |
| 198 | 233 | ||
| 199 | int fpga_region_unregister(struct fpga_region *region) | 234 | /** |
| 235 | * fpga_region_unregister - unregister and free a FPGA region | ||
| 236 | * @region: FPGA region | ||
| 237 | */ | ||
| 238 | void fpga_region_unregister(struct fpga_region *region) | ||
| 200 | { | 239 | { |
| 201 | device_unregister(®ion->dev); | 240 | device_unregister(®ion->dev); |
| 202 | |||
| 203 | return 0; | ||
| 204 | } | 241 | } |
| 205 | EXPORT_SYMBOL_GPL(fpga_region_unregister); | 242 | EXPORT_SYMBOL_GPL(fpga_region_unregister); |
| 206 | 243 | ||
| @@ -208,7 +245,7 @@ static void fpga_region_dev_release(struct device *dev) | |||
| 208 | { | 245 | { |
| 209 | struct fpga_region *region = to_fpga_region(dev); | 246 | struct fpga_region *region = to_fpga_region(dev); |
| 210 | 247 | ||
| 211 | ida_simple_remove(&fpga_region_ida, region->dev.id); | 248 | fpga_region_free(region); |
| 212 | } | 249 | } |
| 213 | 250 | ||
| 214 | /** | 251 | /** |
diff --git a/drivers/fpga/ice40-spi.c b/drivers/fpga/ice40-spi.c index 7fca82023062..5981c7ee7a7d 100644 --- a/drivers/fpga/ice40-spi.c +++ b/drivers/fpga/ice40-spi.c | |||
| @@ -133,6 +133,7 @@ static int ice40_fpga_probe(struct spi_device *spi) | |||
| 133 | { | 133 | { |
| 134 | struct device *dev = &spi->dev; | 134 | struct device *dev = &spi->dev; |
| 135 | struct ice40_fpga_priv *priv; | 135 | struct ice40_fpga_priv *priv; |
| 136 | struct fpga_manager *mgr; | ||
| 136 | int ret; | 137 | int ret; |
| 137 | 138 | ||
| 138 | priv = devm_kzalloc(&spi->dev, sizeof(*priv), GFP_KERNEL); | 139 | priv = devm_kzalloc(&spi->dev, sizeof(*priv), GFP_KERNEL); |
| @@ -174,14 +175,26 @@ static int ice40_fpga_probe(struct spi_device *spi) | |||
| 174 | return ret; | 175 | return ret; |
| 175 | } | 176 | } |
| 176 | 177 | ||
| 177 | /* Register with the FPGA manager */ | 178 | mgr = fpga_mgr_create(dev, "Lattice iCE40 FPGA Manager", |
| 178 | return fpga_mgr_register(dev, "Lattice iCE40 FPGA Manager", | 179 | &ice40_fpga_ops, priv); |
| 179 | &ice40_fpga_ops, priv); | 180 | if (!mgr) |
| 181 | return -ENOMEM; | ||
| 182 | |||
| 183 | spi_set_drvdata(spi, mgr); | ||
| 184 | |||
| 185 | ret = fpga_mgr_register(mgr); | ||
| 186 | if (ret) | ||
| 187 | fpga_mgr_free(mgr); | ||
| 188 | |||
| 189 | return ret; | ||
| 180 | } | 190 | } |
| 181 | 191 | ||
| 182 | static int ice40_fpga_remove(struct spi_device *spi) | 192 | static int ice40_fpga_remove(struct spi_device *spi) |
| 183 | { | 193 | { |
| 184 | fpga_mgr_unregister(&spi->dev); | 194 | struct fpga_manager *mgr = spi_get_drvdata(spi); |
| 195 | |||
| 196 | fpga_mgr_unregister(mgr); | ||
| 197 | |||
| 185 | return 0; | 198 | return 0; |
| 186 | } | 199 | } |
| 187 | 200 | ||
diff --git a/drivers/fpga/machxo2-spi.c b/drivers/fpga/machxo2-spi.c new file mode 100644 index 000000000000..a582e0000c97 --- /dev/null +++ b/drivers/fpga/machxo2-spi.c | |||
| @@ -0,0 +1,415 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 2 | /* | ||
| 3 | * Lattice MachXO2 Slave SPI Driver | ||
| 4 | * | ||
| 5 | * Manage Lattice FPGA firmware that is loaded over SPI using | ||
| 6 | * the slave serial configuration interface. | ||
| 7 | * | ||
| 8 | * Copyright (C) 2018 Paolo Pisati <p.pisati@gmail.com> | ||
| 9 | */ | ||
| 10 | |||
| 11 | #include <linux/delay.h> | ||
| 12 | #include <linux/fpga/fpga-mgr.h> | ||
| 13 | #include <linux/gpio/consumer.h> | ||
| 14 | #include <linux/module.h> | ||
| 15 | #include <linux/of.h> | ||
| 16 | #include <linux/spi/spi.h> | ||
| 17 | |||
| 18 | /* MachXO2 Programming Guide - sysCONFIG Programming Commands */ | ||
| 19 | #define IDCODE_PUB {0xe0, 0x00, 0x00, 0x00} | ||
| 20 | #define ISC_ENABLE {0xc6, 0x08, 0x00, 0x00} | ||
| 21 | #define ISC_ERASE {0x0e, 0x04, 0x00, 0x00} | ||
| 22 | #define ISC_PROGRAMDONE {0x5e, 0x00, 0x00, 0x00} | ||
| 23 | #define LSC_INITADDRESS {0x46, 0x00, 0x00, 0x00} | ||
| 24 | #define LSC_PROGINCRNV {0x70, 0x00, 0x00, 0x01} | ||
| 25 | #define LSC_READ_STATUS {0x3c, 0x00, 0x00, 0x00} | ||
| 26 | #define LSC_REFRESH {0x79, 0x00, 0x00, 0x00} | ||
| 27 | |||
| 28 | /* | ||
| 29 | * Max CCLK in Slave SPI mode according to 'MachXO2 Family Data | ||
| 30 | * Sheet' sysCONFIG Port Timing Specifications (3-36) | ||
| 31 | */ | ||
| 32 | #define MACHXO2_MAX_SPEED 66000000 | ||
| 33 | |||
| 34 | #define MACHXO2_LOW_DELAY_USEC 5 | ||
| 35 | #define MACHXO2_HIGH_DELAY_USEC 200 | ||
| 36 | #define MACHXO2_REFRESH_USEC 4800 | ||
| 37 | #define MACHXO2_MAX_BUSY_LOOP 128 | ||
| 38 | #define MACHXO2_MAX_REFRESH_LOOP 16 | ||
| 39 | |||
| 40 | #define MACHXO2_PAGE_SIZE 16 | ||
| 41 | #define MACHXO2_BUF_SIZE (MACHXO2_PAGE_SIZE + 4) | ||
| 42 | |||
| 43 | /* Status register bits, errors and error mask */ | ||
| 44 | #define BUSY 12 | ||
| 45 | #define DONE 8 | ||
| 46 | #define DVER 27 | ||
| 47 | #define ENAB 9 | ||
| 48 | #define ERRBITS 23 | ||
| 49 | #define ERRMASK 7 | ||
| 50 | #define FAIL 13 | ||
| 51 | |||
| 52 | #define ENOERR 0 /* no error */ | ||
| 53 | #define EID 1 | ||
| 54 | #define ECMD 2 | ||
| 55 | #define ECRC 3 | ||
| 56 | #define EPREAM 4 /* preamble error */ | ||
| 57 | #define EABRT 5 /* abort error */ | ||
| 58 | #define EOVERFL 6 /* overflow error */ | ||
| 59 | #define ESDMEOF 7 /* SDM EOF */ | ||
| 60 | |||
| 61 | static inline u8 get_err(unsigned long *status) | ||
| 62 | { | ||
| 63 | return (*status >> ERRBITS) & ERRMASK; | ||
| 64 | } | ||
| 65 | |||
| 66 | static int get_status(struct spi_device *spi, unsigned long *status) | ||
| 67 | { | ||
| 68 | struct spi_message msg; | ||
| 69 | struct spi_transfer rx, tx; | ||
| 70 | static const u8 cmd[] = LSC_READ_STATUS; | ||
| 71 | int ret; | ||
| 72 | |||
| 73 | memset(&rx, 0, sizeof(rx)); | ||
| 74 | memset(&tx, 0, sizeof(tx)); | ||
| 75 | tx.tx_buf = cmd; | ||
| 76 | tx.len = sizeof(cmd); | ||
| 77 | rx.rx_buf = status; | ||
| 78 | rx.len = 4; | ||
| 79 | spi_message_init(&msg); | ||
| 80 | spi_message_add_tail(&tx, &msg); | ||
| 81 | spi_message_add_tail(&rx, &msg); | ||
| 82 | ret = spi_sync(spi, &msg); | ||
| 83 | if (ret) | ||
| 84 | return ret; | ||
| 85 | |||
| 86 | *status = be32_to_cpu(*status); | ||
| 87 | |||
| 88 | return 0; | ||
| 89 | } | ||
| 90 | |||
| 91 | #ifdef DEBUG | ||
| 92 | static const char *get_err_string(u8 err) | ||
| 93 | { | ||
| 94 | switch (err) { | ||
| 95 | case ENOERR: return "No Error"; | ||
| 96 | case EID: return "ID ERR"; | ||
| 97 | case ECMD: return "CMD ERR"; | ||
| 98 | case ECRC: return "CRC ERR"; | ||
| 99 | case EPREAM: return "Preamble ERR"; | ||
| 100 | case EABRT: return "Abort ERR"; | ||
| 101 | case EOVERFL: return "Overflow ERR"; | ||
| 102 | case ESDMEOF: return "SDM EOF"; | ||
| 103 | } | ||
| 104 | |||
| 105 | return "Default switch case"; | ||
| 106 | } | ||
| 107 | #endif | ||
| 108 | |||
| 109 | static void dump_status_reg(unsigned long *status) | ||
| 110 | { | ||
| 111 | #ifdef DEBUG | ||
| 112 | pr_debug("machxo2 status: 0x%08lX - done=%d, cfgena=%d, busy=%d, fail=%d, devver=%d, err=%s\n", | ||
| 113 | *status, test_bit(DONE, status), test_bit(ENAB, status), | ||
| 114 | test_bit(BUSY, status), test_bit(FAIL, status), | ||
| 115 | test_bit(DVER, status), get_err_string(get_err(status))); | ||
| 116 | #endif | ||
| 117 | } | ||
| 118 | |||
| 119 | static int wait_until_not_busy(struct spi_device *spi) | ||
| 120 | { | ||
| 121 | unsigned long status; | ||
| 122 | int ret, loop = 0; | ||
| 123 | |||
| 124 | do { | ||
| 125 | ret = get_status(spi, &status); | ||
| 126 | if (ret) | ||
| 127 | return ret; | ||
| 128 | if (++loop >= MACHXO2_MAX_BUSY_LOOP) | ||
| 129 | return -EBUSY; | ||
| 130 | } while (test_bit(BUSY, &status)); | ||
| 131 | |||
| 132 | return 0; | ||
| 133 | } | ||
| 134 | |||
| 135 | static int machxo2_cleanup(struct fpga_manager *mgr) | ||
| 136 | { | ||
| 137 | struct spi_device *spi = mgr->priv; | ||
| 138 | struct spi_message msg; | ||
| 139 | struct spi_transfer tx[2]; | ||
| 140 | static const u8 erase[] = ISC_ERASE; | ||
| 141 | static const u8 refresh[] = LSC_REFRESH; | ||
| 142 | int ret; | ||
| 143 | |||
| 144 | memset(tx, 0, sizeof(tx)); | ||
| 145 | spi_message_init(&msg); | ||
| 146 | tx[0].tx_buf = &erase; | ||
| 147 | tx[0].len = sizeof(erase); | ||
| 148 | spi_message_add_tail(&tx[0], &msg); | ||
| 149 | ret = spi_sync(spi, &msg); | ||
| 150 | if (ret) | ||
| 151 | goto fail; | ||
| 152 | |||
| 153 | ret = wait_until_not_busy(spi); | ||
| 154 | if (ret) | ||
| 155 | goto fail; | ||
| 156 | |||
| 157 | spi_message_init(&msg); | ||
| 158 | tx[1].tx_buf = &refresh; | ||
| 159 | tx[1].len = sizeof(refresh); | ||
| 160 | tx[1].delay_usecs = MACHXO2_REFRESH_USEC; | ||
| 161 | spi_message_add_tail(&tx[1], &msg); | ||
| 162 | ret = spi_sync(spi, &msg); | ||
| 163 | if (ret) | ||
| 164 | goto fail; | ||
| 165 | |||
| 166 | return 0; | ||
| 167 | fail: | ||
| 168 | dev_err(&mgr->dev, "Cleanup failed\n"); | ||
| 169 | |||
| 170 | return ret; | ||
| 171 | } | ||
| 172 | |||
| 173 | static enum fpga_mgr_states machxo2_spi_state(struct fpga_manager *mgr) | ||
| 174 | { | ||
| 175 | struct spi_device *spi = mgr->priv; | ||
| 176 | unsigned long status; | ||
| 177 | |||
| 178 | get_status(spi, &status); | ||
| 179 | if (!test_bit(BUSY, &status) && test_bit(DONE, &status) && | ||
| 180 | get_err(&status) == ENOERR) | ||
| 181 | return FPGA_MGR_STATE_OPERATING; | ||
| 182 | |||
| 183 | return FPGA_MGR_STATE_UNKNOWN; | ||
| 184 | } | ||
| 185 | |||
| 186 | static int machxo2_write_init(struct fpga_manager *mgr, | ||
| 187 | struct fpga_image_info *info, | ||
| 188 | const char *buf, size_t count) | ||
| 189 | { | ||
| 190 | struct spi_device *spi = mgr->priv; | ||
| 191 | struct spi_message msg; | ||
| 192 | struct spi_transfer tx[3]; | ||
| 193 | static const u8 enable[] = ISC_ENABLE; | ||
| 194 | static const u8 erase[] = ISC_ERASE; | ||
| 195 | static const u8 initaddr[] = LSC_INITADDRESS; | ||
| 196 | unsigned long status; | ||
| 197 | int ret; | ||
| 198 | |||
| 199 | if ((info->flags & FPGA_MGR_PARTIAL_RECONFIG)) { | ||
| 200 | dev_err(&mgr->dev, | ||
| 201 | "Partial reconfiguration is not supported\n"); | ||
| 202 | return -ENOTSUPP; | ||
| 203 | } | ||
| 204 | |||
| 205 | get_status(spi, &status); | ||
| 206 | dump_status_reg(&status); | ||
| 207 | memset(tx, 0, sizeof(tx)); | ||
| 208 | spi_message_init(&msg); | ||
| 209 | tx[0].tx_buf = &enable; | ||
| 210 | tx[0].len = sizeof(enable); | ||
| 211 | tx[0].delay_usecs = MACHXO2_LOW_DELAY_USEC; | ||
| 212 | spi_message_add_tail(&tx[0], &msg); | ||
| 213 | |||
| 214 | tx[1].tx_buf = &erase; | ||
| 215 | tx[1].len = sizeof(erase); | ||
| 216 | spi_message_add_tail(&tx[1], &msg); | ||
| 217 | ret = spi_sync(spi, &msg); | ||
| 218 | if (ret) | ||
| 219 | goto fail; | ||
| 220 | |||
| 221 | ret = wait_until_not_busy(spi); | ||
| 222 | if (ret) | ||
| 223 | goto fail; | ||
| 224 | |||
| 225 | get_status(spi, &status); | ||
| 226 | if (test_bit(FAIL, &status)) | ||
| 227 | goto fail; | ||
| 228 | dump_status_reg(&status); | ||
| 229 | |||
| 230 | spi_message_init(&msg); | ||
| 231 | tx[2].tx_buf = &initaddr; | ||
| 232 | tx[2].len = sizeof(initaddr); | ||
| 233 | spi_message_add_tail(&tx[2], &msg); | ||
| 234 | ret = spi_sync(spi, &msg); | ||
| 235 | if (ret) | ||
| 236 | goto fail; | ||
| 237 | |||
| 238 | get_status(spi, &status); | ||
| 239 | dump_status_reg(&status); | ||
| 240 | |||
| 241 | return 0; | ||
| 242 | fail: | ||
| 243 | dev_err(&mgr->dev, "Error during FPGA init.\n"); | ||
| 244 | |||
| 245 | return ret; | ||
| 246 | } | ||
| 247 | |||
| 248 | static int machxo2_write(struct fpga_manager *mgr, const char *buf, | ||
| 249 | size_t count) | ||
| 250 | { | ||
| 251 | struct spi_device *spi = mgr->priv; | ||
| 252 | struct spi_message msg; | ||
| 253 | struct spi_transfer tx; | ||
| 254 | static const u8 progincr[] = LSC_PROGINCRNV; | ||
| 255 | u8 payload[MACHXO2_BUF_SIZE]; | ||
| 256 | unsigned long status; | ||
| 257 | int i, ret; | ||
| 258 | |||
| 259 | if (count % MACHXO2_PAGE_SIZE != 0) { | ||
| 260 | dev_err(&mgr->dev, "Malformed payload.\n"); | ||
| 261 | return -EINVAL; | ||
| 262 | } | ||
| 263 | get_status(spi, &status); | ||
| 264 | dump_status_reg(&status); | ||
| 265 | memcpy(payload, &progincr, sizeof(progincr)); | ||
| 266 | for (i = 0; i < count; i += MACHXO2_PAGE_SIZE) { | ||
| 267 | memcpy(&payload[sizeof(progincr)], &buf[i], MACHXO2_PAGE_SIZE); | ||
| 268 | memset(&tx, 0, sizeof(tx)); | ||
| 269 | spi_message_init(&msg); | ||
| 270 | tx.tx_buf = payload; | ||
| 271 | tx.len = MACHXO2_BUF_SIZE; | ||
| 272 | tx.delay_usecs = MACHXO2_HIGH_DELAY_USEC; | ||
| 273 | spi_message_add_tail(&tx, &msg); | ||
| 274 | ret = spi_sync(spi, &msg); | ||
| 275 | if (ret) { | ||
| 276 | dev_err(&mgr->dev, "Error loading the bitstream.\n"); | ||
| 277 | return ret; | ||
| 278 | } | ||
| 279 | } | ||
| 280 | get_status(spi, &status); | ||
| 281 | dump_status_reg(&status); | ||
| 282 | |||
| 283 | return 0; | ||
| 284 | } | ||
| 285 | |||
| 286 | static int machxo2_write_complete(struct fpga_manager *mgr, | ||
| 287 | struct fpga_image_info *info) | ||
| 288 | { | ||
| 289 | struct spi_device *spi = mgr->priv; | ||
| 290 | struct spi_message msg; | ||
| 291 | struct spi_transfer tx[2]; | ||
| 292 | static const u8 progdone[] = ISC_PROGRAMDONE; | ||
| 293 | static const u8 refresh[] = LSC_REFRESH; | ||
| 294 | unsigned long status; | ||
| 295 | int ret, refreshloop = 0; | ||
| 296 | |||
| 297 | memset(tx, 0, sizeof(tx)); | ||
| 298 | spi_message_init(&msg); | ||
| 299 | tx[0].tx_buf = &progdone; | ||
| 300 | tx[0].len = sizeof(progdone); | ||
| 301 | spi_message_add_tail(&tx[0], &msg); | ||
| 302 | ret = spi_sync(spi, &msg); | ||
| 303 | if (ret) | ||
| 304 | goto fail; | ||
| 305 | ret = wait_until_not_busy(spi); | ||
| 306 | if (ret) | ||
| 307 | goto fail; | ||
| 308 | |||
| 309 | get_status(spi, &status); | ||
| 310 | dump_status_reg(&status); | ||
| 311 | if (!test_bit(DONE, &status)) { | ||
| 312 | machxo2_cleanup(mgr); | ||
| 313 | goto fail; | ||
| 314 | } | ||
| 315 | |||
| 316 | do { | ||
| 317 | spi_message_init(&msg); | ||
| 318 | tx[1].tx_buf = &refresh; | ||
| 319 | tx[1].len = sizeof(refresh); | ||
| 320 | tx[1].delay_usecs = MACHXO2_REFRESH_USEC; | ||
| 321 | spi_message_add_tail(&tx[1], &msg); | ||
| 322 | ret = spi_sync(spi, &msg); | ||
| 323 | if (ret) | ||
| 324 | goto fail; | ||
| 325 | |||
| 326 | /* check refresh status */ | ||
| 327 | get_status(spi, &status); | ||
| 328 | dump_status_reg(&status); | ||
| 329 | if (!test_bit(BUSY, &status) && test_bit(DONE, &status) && | ||
| 330 | get_err(&status) == ENOERR) | ||
| 331 | break; | ||
| 332 | if (++refreshloop == MACHXO2_MAX_REFRESH_LOOP) { | ||
| 333 | machxo2_cleanup(mgr); | ||
| 334 | goto fail; | ||
| 335 | } | ||
| 336 | } while (1); | ||
| 337 | |||
| 338 | get_status(spi, &status); | ||
| 339 | dump_status_reg(&status); | ||
| 340 | |||
| 341 | return 0; | ||
| 342 | fail: | ||
| 343 | dev_err(&mgr->dev, "Refresh failed.\n"); | ||
| 344 | |||
| 345 | return ret; | ||
| 346 | } | ||
| 347 | |||
| 348 | static const struct fpga_manager_ops machxo2_ops = { | ||
| 349 | .state = machxo2_spi_state, | ||
| 350 | .write_init = machxo2_write_init, | ||
| 351 | .write = machxo2_write, | ||
| 352 | .write_complete = machxo2_write_complete, | ||
| 353 | }; | ||
| 354 | |||
| 355 | static int machxo2_spi_probe(struct spi_device *spi) | ||
| 356 | { | ||
| 357 | struct device *dev = &spi->dev; | ||
| 358 | struct fpga_manager *mgr; | ||
| 359 | int ret; | ||
| 360 | |||
| 361 | if (spi->max_speed_hz > MACHXO2_MAX_SPEED) { | ||
| 362 | dev_err(dev, "Speed is too high\n"); | ||
| 363 | return -EINVAL; | ||
| 364 | } | ||
| 365 | |||
| 366 | mgr = fpga_mgr_create(dev, "Lattice MachXO2 SPI FPGA Manager", | ||
| 367 | &machxo2_ops, spi); | ||
| 368 | if (!mgr) | ||
| 369 | return -ENOMEM; | ||
| 370 | |||
| 371 | spi_set_drvdata(spi, mgr); | ||
| 372 | |||
| 373 | ret = fpga_mgr_register(mgr); | ||
| 374 | if (ret) | ||
| 375 | fpga_mgr_free(mgr); | ||
| 376 | |||
| 377 | return ret; | ||
| 378 | } | ||
| 379 | |||
| 380 | static int machxo2_spi_remove(struct spi_device *spi) | ||
| 381 | { | ||
| 382 | struct fpga_manager *mgr = spi_get_drvdata(spi); | ||
| 383 | |||
| 384 | fpga_mgr_unregister(mgr); | ||
| 385 | |||
| 386 | return 0; | ||
| 387 | } | ||
| 388 | |||
| 389 | static const struct of_device_id of_match[] = { | ||
| 390 | { .compatible = "lattice,machxo2-slave-spi", }, | ||
| 391 | {} | ||
| 392 | }; | ||
| 393 | MODULE_DEVICE_TABLE(of, of_match); | ||
| 394 | |||
| 395 | static const struct spi_device_id lattice_ids[] = { | ||
| 396 | { "machxo2-slave-spi", 0 }, | ||
| 397 | { }, | ||
| 398 | }; | ||
| 399 | MODULE_DEVICE_TABLE(spi, lattice_ids); | ||
| 400 | |||
| 401 | static struct spi_driver machxo2_spi_driver = { | ||
| 402 | .driver = { | ||
| 403 | .name = "machxo2-slave-spi", | ||
| 404 | .of_match_table = of_match_ptr(of_match), | ||
| 405 | }, | ||
| 406 | .probe = machxo2_spi_probe, | ||
| 407 | .remove = machxo2_spi_remove, | ||
| 408 | .id_table = lattice_ids, | ||
| 409 | }; | ||
| 410 | |||
| 411 | module_spi_driver(machxo2_spi_driver) | ||
| 412 | |||
| 413 | MODULE_AUTHOR("Paolo Pisati <p.pisati@gmail.com>"); | ||
| 414 | MODULE_DESCRIPTION("Load Lattice FPGA firmware over SPI"); | ||
| 415 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/drivers/fpga/of-fpga-region.c b/drivers/fpga/of-fpga-region.c index 119ff75522f1..35fabb8083fb 100644 --- a/drivers/fpga/of-fpga-region.c +++ b/drivers/fpga/of-fpga-region.c | |||
| @@ -1,22 +1,10 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 1 | /* | 2 | /* |
| 2 | * FPGA Region - Device Tree support for FPGA programming under Linux | 3 | * FPGA Region - Device Tree support for FPGA programming under Linux |
| 3 | * | 4 | * |
| 4 | * Copyright (C) 2013-2016 Altera Corporation | 5 | * Copyright (C) 2013-2016 Altera Corporation |
| 5 | * Copyright (C) 2017 Intel Corporation | 6 | * Copyright (C) 2017 Intel Corporation |
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or modify it | ||
| 8 | * under the terms and conditions of the GNU General Public License, | ||
| 9 | * version 2, as published by the Free Software Foundation. | ||
| 10 | * | ||
| 11 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 14 | * more details. | ||
| 15 | * | ||
| 16 | * You should have received a copy of the GNU General Public License along with | ||
| 17 | * this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 18 | */ | 7 | */ |
| 19 | |||
| 20 | #include <linux/fpga/fpga-bridge.h> | 8 | #include <linux/fpga/fpga-bridge.h> |
| 21 | #include <linux/fpga/fpga-mgr.h> | 9 | #include <linux/fpga/fpga-mgr.h> |
| 22 | #include <linux/fpga/fpga-region.h> | 10 | #include <linux/fpga/fpga-region.h> |
| @@ -422,27 +410,25 @@ static int of_fpga_region_probe(struct platform_device *pdev) | |||
| 422 | if (IS_ERR(mgr)) | 410 | if (IS_ERR(mgr)) |
| 423 | return -EPROBE_DEFER; | 411 | return -EPROBE_DEFER; |
| 424 | 412 | ||
| 425 | region = devm_kzalloc(dev, sizeof(*region), GFP_KERNEL); | 413 | region = fpga_region_create(dev, mgr, of_fpga_region_get_bridges); |
| 426 | if (!region) { | 414 | if (!region) { |
| 427 | ret = -ENOMEM; | 415 | ret = -ENOMEM; |
| 428 | goto eprobe_mgr_put; | 416 | goto eprobe_mgr_put; |
| 429 | } | 417 | } |
| 430 | 418 | ||
| 431 | region->mgr = mgr; | 419 | ret = fpga_region_register(region); |
| 432 | |||
| 433 | /* Specify how to get bridges for this type of region. */ | ||
| 434 | region->get_bridges = of_fpga_region_get_bridges; | ||
| 435 | |||
| 436 | ret = fpga_region_register(dev, region); | ||
| 437 | if (ret) | 420 | if (ret) |
| 438 | goto eprobe_mgr_put; | 421 | goto eprobe_free; |
| 439 | 422 | ||
| 440 | of_platform_populate(np, fpga_region_of_match, NULL, ®ion->dev); | 423 | of_platform_populate(np, fpga_region_of_match, NULL, ®ion->dev); |
| 424 | dev_set_drvdata(dev, region); | ||
| 441 | 425 | ||
| 442 | dev_info(dev, "FPGA Region probed\n"); | 426 | dev_info(dev, "FPGA Region probed\n"); |
| 443 | 427 | ||
| 444 | return 0; | 428 | return 0; |
| 445 | 429 | ||
| 430 | eprobe_free: | ||
| 431 | fpga_region_free(region); | ||
| 446 | eprobe_mgr_put: | 432 | eprobe_mgr_put: |
| 447 | fpga_mgr_put(mgr); | 433 | fpga_mgr_put(mgr); |
| 448 | return ret; | 434 | return ret; |
diff --git a/drivers/fpga/socfpga-a10.c b/drivers/fpga/socfpga-a10.c index a46e343a5b72..be30c48eb6e4 100644 --- a/drivers/fpga/socfpga-a10.c +++ b/drivers/fpga/socfpga-a10.c | |||
| @@ -1,21 +1,9 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 1 | /* | 2 | /* |
| 2 | * FPGA Manager Driver for Altera Arria10 SoCFPGA | 3 | * FPGA Manager Driver for Altera Arria10 SoCFPGA |
| 3 | * | 4 | * |
| 4 | * Copyright (C) 2015-2016 Altera Corporation | 5 | * Copyright (C) 2015-2016 Altera Corporation |
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify it | ||
| 7 | * under the terms and conditions of the GNU General Public License, | ||
| 8 | * version 2, as published by the Free Software Foundation. | ||
| 9 | * | ||
| 10 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
| 11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 13 | * more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU General Public License along with | ||
| 16 | * this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 17 | */ | 6 | */ |
| 18 | |||
| 19 | #include <linux/clk.h> | 7 | #include <linux/clk.h> |
| 20 | #include <linux/device.h> | 8 | #include <linux/device.h> |
| 21 | #include <linux/delay.h> | 9 | #include <linux/delay.h> |
| @@ -482,6 +470,7 @@ static int socfpga_a10_fpga_probe(struct platform_device *pdev) | |||
| 482 | struct device *dev = &pdev->dev; | 470 | struct device *dev = &pdev->dev; |
| 483 | struct a10_fpga_priv *priv; | 471 | struct a10_fpga_priv *priv; |
| 484 | void __iomem *reg_base; | 472 | void __iomem *reg_base; |
| 473 | struct fpga_manager *mgr; | ||
| 485 | struct resource *res; | 474 | struct resource *res; |
| 486 | int ret; | 475 | int ret; |
| 487 | 476 | ||
| @@ -519,9 +508,16 @@ static int socfpga_a10_fpga_probe(struct platform_device *pdev) | |||
| 519 | return -EBUSY; | 508 | return -EBUSY; |
| 520 | } | 509 | } |
| 521 | 510 | ||
| 522 | ret = fpga_mgr_register(dev, "SoCFPGA Arria10 FPGA Manager", | 511 | mgr = fpga_mgr_create(dev, "SoCFPGA Arria10 FPGA Manager", |
| 523 | &socfpga_a10_fpga_mgr_ops, priv); | 512 | &socfpga_a10_fpga_mgr_ops, priv); |
| 513 | if (!mgr) | ||
| 514 | return -ENOMEM; | ||
| 515 | |||
| 516 | platform_set_drvdata(pdev, mgr); | ||
| 517 | |||
| 518 | ret = fpga_mgr_register(mgr); | ||
| 524 | if (ret) { | 519 | if (ret) { |
| 520 | fpga_mgr_free(mgr); | ||
| 525 | clk_disable_unprepare(priv->clk); | 521 | clk_disable_unprepare(priv->clk); |
| 526 | return ret; | 522 | return ret; |
| 527 | } | 523 | } |
| @@ -534,7 +530,7 @@ static int socfpga_a10_fpga_remove(struct platform_device *pdev) | |||
| 534 | struct fpga_manager *mgr = platform_get_drvdata(pdev); | 530 | struct fpga_manager *mgr = platform_get_drvdata(pdev); |
| 535 | struct a10_fpga_priv *priv = mgr->priv; | 531 | struct a10_fpga_priv *priv = mgr->priv; |
| 536 | 532 | ||
| 537 | fpga_mgr_unregister(&pdev->dev); | 533 | fpga_mgr_unregister(mgr); |
| 538 | clk_disable_unprepare(priv->clk); | 534 | clk_disable_unprepare(priv->clk); |
| 539 | 535 | ||
| 540 | return 0; | 536 | return 0; |
diff --git a/drivers/fpga/socfpga.c b/drivers/fpga/socfpga.c index b6672e66cda6..959d71f26896 100644 --- a/drivers/fpga/socfpga.c +++ b/drivers/fpga/socfpga.c | |||
| @@ -1,19 +1,8 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 1 | /* | 2 | /* |
| 2 | * FPGA Manager Driver for Altera SOCFPGA | 3 | * FPGA Manager Driver for Altera SOCFPGA |
| 3 | * | 4 | * |
| 4 | * Copyright (C) 2013-2015 Altera Corporation | 5 | * Copyright (C) 2013-2015 Altera Corporation |
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify it | ||
| 7 | * under the terms and conditions of the GNU General Public License, | ||
| 8 | * version 2, as published by the Free Software Foundation. | ||
| 9 | * | ||
| 10 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
| 11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 13 | * more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU General Public License along with | ||
| 16 | * this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 17 | */ | 6 | */ |
| 18 | #include <linux/completion.h> | 7 | #include <linux/completion.h> |
| 19 | #include <linux/delay.h> | 8 | #include <linux/delay.h> |
| @@ -555,6 +544,7 @@ static int socfpga_fpga_probe(struct platform_device *pdev) | |||
| 555 | { | 544 | { |
| 556 | struct device *dev = &pdev->dev; | 545 | struct device *dev = &pdev->dev; |
| 557 | struct socfpga_fpga_priv *priv; | 546 | struct socfpga_fpga_priv *priv; |
| 547 | struct fpga_manager *mgr; | ||
| 558 | struct resource *res; | 548 | struct resource *res; |
| 559 | int ret; | 549 | int ret; |
| 560 | 550 | ||
| @@ -581,13 +571,25 @@ static int socfpga_fpga_probe(struct platform_device *pdev) | |||
| 581 | if (ret) | 571 | if (ret) |
| 582 | return ret; | 572 | return ret; |
| 583 | 573 | ||
| 584 | return fpga_mgr_register(dev, "Altera SOCFPGA FPGA Manager", | 574 | mgr = fpga_mgr_create(dev, "Altera SOCFPGA FPGA Manager", |
| 585 | &socfpga_fpga_ops, priv); | 575 | &socfpga_fpga_ops, priv); |
| 576 | if (!mgr) | ||
| 577 | return -ENOMEM; | ||
| 578 | |||
| 579 | platform_set_drvdata(pdev, mgr); | ||
| 580 | |||
| 581 | ret = fpga_mgr_register(mgr); | ||
| 582 | if (ret) | ||
| 583 | fpga_mgr_free(mgr); | ||
| 584 | |||
| 585 | return ret; | ||
| 586 | } | 586 | } |
| 587 | 587 | ||
| 588 | static int socfpga_fpga_remove(struct platform_device *pdev) | 588 | static int socfpga_fpga_remove(struct platform_device *pdev) |
| 589 | { | 589 | { |
| 590 | fpga_mgr_unregister(&pdev->dev); | 590 | struct fpga_manager *mgr = platform_get_drvdata(pdev); |
| 591 | |||
| 592 | fpga_mgr_unregister(mgr); | ||
| 591 | 593 | ||
| 592 | return 0; | 594 | return 0; |
| 593 | } | 595 | } |
diff --git a/drivers/fpga/ts73xx-fpga.c b/drivers/fpga/ts73xx-fpga.c index f6a96b42e2ca..08efd1895b1b 100644 --- a/drivers/fpga/ts73xx-fpga.c +++ b/drivers/fpga/ts73xx-fpga.c | |||
| @@ -116,7 +116,9 @@ static int ts73xx_fpga_probe(struct platform_device *pdev) | |||
| 116 | { | 116 | { |
| 117 | struct device *kdev = &pdev->dev; | 117 | struct device *kdev = &pdev->dev; |
| 118 | struct ts73xx_fpga_priv *priv; | 118 | struct ts73xx_fpga_priv *priv; |
| 119 | struct fpga_manager *mgr; | ||
| 119 | struct resource *res; | 120 | struct resource *res; |
| 121 | int ret; | ||
| 120 | 122 | ||
| 121 | priv = devm_kzalloc(kdev, sizeof(*priv), GFP_KERNEL); | 123 | priv = devm_kzalloc(kdev, sizeof(*priv), GFP_KERNEL); |
| 122 | if (!priv) | 124 | if (!priv) |
| @@ -131,13 +133,25 @@ static int ts73xx_fpga_probe(struct platform_device *pdev) | |||
| 131 | return PTR_ERR(priv->io_base); | 133 | return PTR_ERR(priv->io_base); |
| 132 | } | 134 | } |
| 133 | 135 | ||
| 134 | return fpga_mgr_register(kdev, "TS-73xx FPGA Manager", | 136 | mgr = fpga_mgr_create(kdev, "TS-73xx FPGA Manager", |
| 135 | &ts73xx_fpga_ops, priv); | 137 | &ts73xx_fpga_ops, priv); |
| 138 | if (!mgr) | ||
| 139 | return -ENOMEM; | ||
| 140 | |||
| 141 | platform_set_drvdata(pdev, mgr); | ||
| 142 | |||
| 143 | ret = fpga_mgr_register(mgr); | ||
| 144 | if (ret) | ||
| 145 | fpga_mgr_free(mgr); | ||
| 146 | |||
| 147 | return ret; | ||
| 136 | } | 148 | } |
| 137 | 149 | ||
| 138 | static int ts73xx_fpga_remove(struct platform_device *pdev) | 150 | static int ts73xx_fpga_remove(struct platform_device *pdev) |
| 139 | { | 151 | { |
| 140 | fpga_mgr_unregister(&pdev->dev); | 152 | struct fpga_manager *mgr = platform_get_drvdata(pdev); |
| 153 | |||
| 154 | fpga_mgr_unregister(mgr); | ||
| 141 | 155 | ||
| 142 | return 0; | 156 | return 0; |
| 143 | } | 157 | } |
diff --git a/drivers/fpga/xilinx-pr-decoupler.c b/drivers/fpga/xilinx-pr-decoupler.c index 0d7743089414..07ba1539e82c 100644 --- a/drivers/fpga/xilinx-pr-decoupler.c +++ b/drivers/fpga/xilinx-pr-decoupler.c | |||
| @@ -94,6 +94,7 @@ MODULE_DEVICE_TABLE(of, xlnx_pr_decoupler_of_match); | |||
| 94 | static int xlnx_pr_decoupler_probe(struct platform_device *pdev) | 94 | static int xlnx_pr_decoupler_probe(struct platform_device *pdev) |
| 95 | { | 95 | { |
| 96 | struct xlnx_pr_decoupler_data *priv; | 96 | struct xlnx_pr_decoupler_data *priv; |
| 97 | struct fpga_bridge *br; | ||
| 97 | int err; | 98 | int err; |
| 98 | struct resource *res; | 99 | struct resource *res; |
| 99 | 100 | ||
| @@ -120,16 +121,27 @@ static int xlnx_pr_decoupler_probe(struct platform_device *pdev) | |||
| 120 | 121 | ||
| 121 | clk_disable(priv->clk); | 122 | clk_disable(priv->clk); |
| 122 | 123 | ||
| 123 | err = fpga_bridge_register(&pdev->dev, "Xilinx PR Decoupler", | 124 | br = fpga_bridge_create(&pdev->dev, "Xilinx PR Decoupler", |
| 124 | &xlnx_pr_decoupler_br_ops, priv); | 125 | &xlnx_pr_decoupler_br_ops, priv); |
| 126 | if (!br) { | ||
| 127 | err = -ENOMEM; | ||
| 128 | goto err_clk; | ||
| 129 | } | ||
| 130 | |||
| 131 | platform_set_drvdata(pdev, br); | ||
| 125 | 132 | ||
| 133 | err = fpga_bridge_register(br); | ||
| 126 | if (err) { | 134 | if (err) { |
| 127 | dev_err(&pdev->dev, "unable to register Xilinx PR Decoupler"); | 135 | dev_err(&pdev->dev, "unable to register Xilinx PR Decoupler"); |
| 128 | clk_unprepare(priv->clk); | 136 | goto err_clk; |
| 129 | return err; | ||
| 130 | } | 137 | } |
| 131 | 138 | ||
| 132 | return 0; | 139 | return 0; |
| 140 | |||
| 141 | err_clk: | ||
| 142 | clk_unprepare(priv->clk); | ||
| 143 | |||
| 144 | return err; | ||
| 133 | } | 145 | } |
| 134 | 146 | ||
| 135 | static int xlnx_pr_decoupler_remove(struct platform_device *pdev) | 147 | static int xlnx_pr_decoupler_remove(struct platform_device *pdev) |
| @@ -137,7 +149,7 @@ static int xlnx_pr_decoupler_remove(struct platform_device *pdev) | |||
| 137 | struct fpga_bridge *bridge = platform_get_drvdata(pdev); | 149 | struct fpga_bridge *bridge = platform_get_drvdata(pdev); |
| 138 | struct xlnx_pr_decoupler_data *p = bridge->priv; | 150 | struct xlnx_pr_decoupler_data *p = bridge->priv; |
| 139 | 151 | ||
| 140 | fpga_bridge_unregister(&pdev->dev); | 152 | fpga_bridge_unregister(bridge); |
| 141 | 153 | ||
| 142 | clk_unprepare(p->clk); | 154 | clk_unprepare(p->clk); |
| 143 | 155 | ||
diff --git a/drivers/fpga/xilinx-spi.c b/drivers/fpga/xilinx-spi.c index 9b62a4c2a3df..8d1945966533 100644 --- a/drivers/fpga/xilinx-spi.c +++ b/drivers/fpga/xilinx-spi.c | |||
| @@ -143,6 +143,8 @@ static const struct fpga_manager_ops xilinx_spi_ops = { | |||
| 143 | static int xilinx_spi_probe(struct spi_device *spi) | 143 | static int xilinx_spi_probe(struct spi_device *spi) |
| 144 | { | 144 | { |
| 145 | struct xilinx_spi_conf *conf; | 145 | struct xilinx_spi_conf *conf; |
| 146 | struct fpga_manager *mgr; | ||
| 147 | int ret; | ||
| 146 | 148 | ||
| 147 | conf = devm_kzalloc(&spi->dev, sizeof(*conf), GFP_KERNEL); | 149 | conf = devm_kzalloc(&spi->dev, sizeof(*conf), GFP_KERNEL); |
| 148 | if (!conf) | 150 | if (!conf) |
| @@ -165,13 +167,25 @@ static int xilinx_spi_probe(struct spi_device *spi) | |||
| 165 | return PTR_ERR(conf->done); | 167 | return PTR_ERR(conf->done); |
| 166 | } | 168 | } |
| 167 | 169 | ||
| 168 | return fpga_mgr_register(&spi->dev, "Xilinx Slave Serial FPGA Manager", | 170 | mgr = fpga_mgr_create(&spi->dev, "Xilinx Slave Serial FPGA Manager", |
| 169 | &xilinx_spi_ops, conf); | 171 | &xilinx_spi_ops, conf); |
| 172 | if (!mgr) | ||
| 173 | return -ENOMEM; | ||
| 174 | |||
| 175 | spi_set_drvdata(spi, mgr); | ||
| 176 | |||
| 177 | ret = fpga_mgr_register(mgr); | ||
| 178 | if (ret) | ||
| 179 | fpga_mgr_free(mgr); | ||
| 180 | |||
| 181 | return ret; | ||
| 170 | } | 182 | } |
| 171 | 183 | ||
| 172 | static int xilinx_spi_remove(struct spi_device *spi) | 184 | static int xilinx_spi_remove(struct spi_device *spi) |
| 173 | { | 185 | { |
| 174 | fpga_mgr_unregister(&spi->dev); | 186 | struct fpga_manager *mgr = spi_get_drvdata(spi); |
| 187 | |||
| 188 | fpga_mgr_unregister(mgr); | ||
| 175 | 189 | ||
| 176 | return 0; | 190 | return 0; |
| 177 | } | 191 | } |
diff --git a/drivers/fpga/zynq-fpga.c b/drivers/fpga/zynq-fpga.c index 70b15b303471..3110e00121ca 100644 --- a/drivers/fpga/zynq-fpga.c +++ b/drivers/fpga/zynq-fpga.c | |||
| @@ -558,6 +558,7 @@ static int zynq_fpga_probe(struct platform_device *pdev) | |||
| 558 | { | 558 | { |
| 559 | struct device *dev = &pdev->dev; | 559 | struct device *dev = &pdev->dev; |
| 560 | struct zynq_fpga_priv *priv; | 560 | struct zynq_fpga_priv *priv; |
| 561 | struct fpga_manager *mgr; | ||
| 561 | struct resource *res; | 562 | struct resource *res; |
| 562 | int err; | 563 | int err; |
| 563 | 564 | ||
| @@ -613,10 +614,17 @@ static int zynq_fpga_probe(struct platform_device *pdev) | |||
| 613 | 614 | ||
| 614 | clk_disable(priv->clk); | 615 | clk_disable(priv->clk); |
| 615 | 616 | ||
| 616 | err = fpga_mgr_register(dev, "Xilinx Zynq FPGA Manager", | 617 | mgr = fpga_mgr_create(dev, "Xilinx Zynq FPGA Manager", |
| 617 | &zynq_fpga_ops, priv); | 618 | &zynq_fpga_ops, priv); |
| 619 | if (!mgr) | ||
| 620 | return -ENOMEM; | ||
| 621 | |||
| 622 | platform_set_drvdata(pdev, mgr); | ||
| 623 | |||
| 624 | err = fpga_mgr_register(mgr); | ||
| 618 | if (err) { | 625 | if (err) { |
| 619 | dev_err(dev, "unable to register FPGA manager\n"); | 626 | dev_err(dev, "unable to register FPGA manager\n"); |
| 627 | fpga_mgr_free(mgr); | ||
| 620 | clk_unprepare(priv->clk); | 628 | clk_unprepare(priv->clk); |
| 621 | return err; | 629 | return err; |
| 622 | } | 630 | } |
| @@ -632,7 +640,7 @@ static int zynq_fpga_remove(struct platform_device *pdev) | |||
| 632 | mgr = platform_get_drvdata(pdev); | 640 | mgr = platform_get_drvdata(pdev); |
| 633 | priv = mgr->priv; | 641 | priv = mgr->priv; |
| 634 | 642 | ||
| 635 | fpga_mgr_unregister(&pdev->dev); | 643 | fpga_mgr_unregister(mgr); |
| 636 | 644 | ||
| 637 | clk_unprepare(priv->clk); | 645 | clk_unprepare(priv->clk); |
| 638 | 646 | ||
diff --git a/drivers/hv/connection.c b/drivers/hv/connection.c index 72855182b191..ced041899456 100644 --- a/drivers/hv/connection.c +++ b/drivers/hv/connection.c | |||
| @@ -63,6 +63,9 @@ static __u32 vmbus_get_next_version(__u32 current_version) | |||
| 63 | case (VERSION_WIN10): | 63 | case (VERSION_WIN10): |
| 64 | return VERSION_WIN8_1; | 64 | return VERSION_WIN8_1; |
| 65 | 65 | ||
| 66 | case (VERSION_WIN10_V5): | ||
| 67 | return VERSION_WIN10; | ||
| 68 | |||
| 66 | case (VERSION_WS2008): | 69 | case (VERSION_WS2008): |
| 67 | default: | 70 | default: |
| 68 | return VERSION_INVAL; | 71 | return VERSION_INVAL; |
| @@ -80,9 +83,29 @@ static int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo, | |||
| 80 | 83 | ||
| 81 | msg = (struct vmbus_channel_initiate_contact *)msginfo->msg; | 84 | msg = (struct vmbus_channel_initiate_contact *)msginfo->msg; |
| 82 | 85 | ||
| 86 | memset(msg, 0, sizeof(*msg)); | ||
| 83 | msg->header.msgtype = CHANNELMSG_INITIATE_CONTACT; | 87 | msg->header.msgtype = CHANNELMSG_INITIATE_CONTACT; |
| 84 | msg->vmbus_version_requested = version; | 88 | msg->vmbus_version_requested = version; |
| 85 | msg->interrupt_page = virt_to_phys(vmbus_connection.int_page); | 89 | |
| 90 | /* | ||
| 91 | * VMBus protocol 5.0 (VERSION_WIN10_V5) requires that we must use | ||
| 92 | * VMBUS_MESSAGE_CONNECTION_ID_4 for the Initiate Contact Message, | ||
| 93 | * and for subsequent messages, we must use the Message Connection ID | ||
| 94 | * field in the host-returned Version Response Message. And, with | ||
| 95 | * VERSION_WIN10_V5, we don't use msg->interrupt_page, but we tell | ||
| 96 | * the host explicitly that we still use VMBUS_MESSAGE_SINT(2) for | ||
| 97 | * compatibility. | ||
| 98 | * | ||
| 99 | * On old hosts, we should always use VMBUS_MESSAGE_CONNECTION_ID (1). | ||
| 100 | */ | ||
| 101 | if (version >= VERSION_WIN10_V5) { | ||
| 102 | msg->msg_sint = VMBUS_MESSAGE_SINT; | ||
| 103 | vmbus_connection.msg_conn_id = VMBUS_MESSAGE_CONNECTION_ID_4; | ||
| 104 | } else { | ||
| 105 | msg->interrupt_page = virt_to_phys(vmbus_connection.int_page); | ||
| 106 | vmbus_connection.msg_conn_id = VMBUS_MESSAGE_CONNECTION_ID; | ||
| 107 | } | ||
| 108 | |||
| 86 | msg->monitor_page1 = virt_to_phys(vmbus_connection.monitor_pages[0]); | 109 | msg->monitor_page1 = virt_to_phys(vmbus_connection.monitor_pages[0]); |
| 87 | msg->monitor_page2 = virt_to_phys(vmbus_connection.monitor_pages[1]); | 110 | msg->monitor_page2 = virt_to_phys(vmbus_connection.monitor_pages[1]); |
| 88 | /* | 111 | /* |
| @@ -137,6 +160,10 @@ static int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo, | |||
| 137 | /* Check if successful */ | 160 | /* Check if successful */ |
| 138 | if (msginfo->response.version_response.version_supported) { | 161 | if (msginfo->response.version_response.version_supported) { |
| 139 | vmbus_connection.conn_state = CONNECTED; | 162 | vmbus_connection.conn_state = CONNECTED; |
| 163 | |||
| 164 | if (version >= VERSION_WIN10_V5) | ||
| 165 | vmbus_connection.msg_conn_id = | ||
| 166 | msginfo->response.version_response.msg_conn_id; | ||
| 140 | } else { | 167 | } else { |
| 141 | return -ECONNREFUSED; | 168 | return -ECONNREFUSED; |
| 142 | } | 169 | } |
| @@ -354,13 +381,14 @@ void vmbus_on_event(unsigned long data) | |||
| 354 | */ | 381 | */ |
| 355 | int vmbus_post_msg(void *buffer, size_t buflen, bool can_sleep) | 382 | int vmbus_post_msg(void *buffer, size_t buflen, bool can_sleep) |
| 356 | { | 383 | { |
| 384 | struct vmbus_channel_message_header *hdr; | ||
| 357 | union hv_connection_id conn_id; | 385 | union hv_connection_id conn_id; |
| 358 | int ret = 0; | 386 | int ret = 0; |
| 359 | int retries = 0; | 387 | int retries = 0; |
| 360 | u32 usec = 1; | 388 | u32 usec = 1; |
| 361 | 389 | ||
| 362 | conn_id.asu32 = 0; | 390 | conn_id.asu32 = 0; |
| 363 | conn_id.u.id = VMBUS_MESSAGE_CONNECTION_ID; | 391 | conn_id.u.id = vmbus_connection.msg_conn_id; |
| 364 | 392 | ||
| 365 | /* | 393 | /* |
| 366 | * hv_post_message() can have transient failures because of | 394 | * hv_post_message() can have transient failures because of |
| @@ -373,6 +401,18 @@ int vmbus_post_msg(void *buffer, size_t buflen, bool can_sleep) | |||
| 373 | switch (ret) { | 401 | switch (ret) { |
| 374 | case HV_STATUS_INVALID_CONNECTION_ID: | 402 | case HV_STATUS_INVALID_CONNECTION_ID: |
| 375 | /* | 403 | /* |
| 404 | * See vmbus_negotiate_version(): VMBus protocol 5.0 | ||
| 405 | * requires that we must use | ||
| 406 | * VMBUS_MESSAGE_CONNECTION_ID_4 for the Initiate | ||
| 407 | * Contact message, but on old hosts that only | ||
| 408 | * support VMBus protocol 4.0 or lower, here we get | ||
| 409 | * HV_STATUS_INVALID_CONNECTION_ID and we should | ||
| 410 | * return an error immediately without retrying. | ||
| 411 | */ | ||
| 412 | hdr = buffer; | ||
| 413 | if (hdr->msgtype == CHANNELMSG_INITIATE_CONTACT) | ||
| 414 | return -EINVAL; | ||
| 415 | /* | ||
| 376 | * We could get this if we send messages too | 416 | * We could get this if we send messages too |
| 377 | * frequently. | 417 | * frequently. |
| 378 | */ | 418 | */ |
diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h index f761bef36e77..72eaba3d50fc 100644 --- a/drivers/hv/hyperv_vmbus.h +++ b/drivers/hv/hyperv_vmbus.h | |||
| @@ -187,6 +187,7 @@ struct hv_input_post_message { | |||
| 187 | 187 | ||
| 188 | enum { | 188 | enum { |
| 189 | VMBUS_MESSAGE_CONNECTION_ID = 1, | 189 | VMBUS_MESSAGE_CONNECTION_ID = 1, |
| 190 | VMBUS_MESSAGE_CONNECTION_ID_4 = 4, | ||
| 190 | VMBUS_MESSAGE_PORT_ID = 1, | 191 | VMBUS_MESSAGE_PORT_ID = 1, |
| 191 | VMBUS_EVENT_CONNECTION_ID = 2, | 192 | VMBUS_EVENT_CONNECTION_ID = 2, |
| 192 | VMBUS_EVENT_PORT_ID = 2, | 193 | VMBUS_EVENT_PORT_ID = 2, |
| @@ -302,6 +303,8 @@ struct vmbus_connection { | |||
| 302 | */ | 303 | */ |
| 303 | int connect_cpu; | 304 | int connect_cpu; |
| 304 | 305 | ||
| 306 | u32 msg_conn_id; | ||
| 307 | |||
| 305 | atomic_t offer_in_progress; | 308 | atomic_t offer_in_progress; |
| 306 | 309 | ||
| 307 | enum vmbus_connect_state conn_state; | 310 | enum vmbus_connect_state conn_state; |
diff --git a/drivers/hwtracing/coresight/coresight-cpu-debug.c b/drivers/hwtracing/coresight/coresight-cpu-debug.c index 9cdb3fbc8c1f..45b2460f3166 100644 --- a/drivers/hwtracing/coresight/coresight-cpu-debug.c +++ b/drivers/hwtracing/coresight/coresight-cpu-debug.c | |||
| @@ -1,20 +1,8 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 1 | /* | 2 | /* |
| 2 | * Copyright (c) 2017 Linaro Limited. All rights reserved. | 3 | * Copyright (c) 2017 Linaro Limited. All rights reserved. |
| 3 | * | 4 | * |
| 4 | * Author: Leo Yan <leo.yan@linaro.org> | 5 | * Author: Leo Yan <leo.yan@linaro.org> |
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify it | ||
| 7 | * under the terms of the GNU General Public License version 2 as published by | ||
| 8 | * the Free Software Foundation. | ||
| 9 | * | ||
| 10 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
| 11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 13 | * more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU General Public License along with | ||
| 16 | * this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 17 | * | ||
| 18 | */ | 6 | */ |
| 19 | #include <linux/amba/bus.h> | 7 | #include <linux/amba/bus.h> |
| 20 | #include <linux/coresight.h> | 8 | #include <linux/coresight.h> |
| @@ -315,7 +303,7 @@ static void debug_dump_regs(struct debug_drvdata *drvdata) | |||
| 315 | } | 303 | } |
| 316 | 304 | ||
| 317 | pc = debug_adjust_pc(drvdata); | 305 | pc = debug_adjust_pc(drvdata); |
| 318 | dev_emerg(dev, " EDPCSR: [<%px>] %pS\n", (void *)pc, (void *)pc); | 306 | dev_emerg(dev, " EDPCSR: %pS\n", (void *)pc); |
| 319 | 307 | ||
| 320 | if (drvdata->edcidsr_present) | 308 | if (drvdata->edcidsr_present) |
| 321 | dev_emerg(dev, " EDCIDSR: %08x\n", drvdata->edcidsr); | 309 | dev_emerg(dev, " EDCIDSR: %08x\n", drvdata->edcidsr); |
diff --git a/drivers/hwtracing/coresight/coresight-dynamic-replicator.c b/drivers/hwtracing/coresight/coresight-dynamic-replicator.c index 043da86b0fe9..f6d0571ab9dd 100644 --- a/drivers/hwtracing/coresight/coresight-dynamic-replicator.c +++ b/drivers/hwtracing/coresight/coresight-dynamic-replicator.c | |||
| @@ -1,14 +1,6 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 1 | /* | 2 | /* |
| 2 | * Copyright (c) 2011-2015, The Linux Foundation. All rights reserved. | 3 | * Copyright (c) 2011-2015, The Linux Foundation. All rights reserved. |
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or modify | ||
| 5 | * it under the terms of the GNU General Public License version 2 and | ||
| 6 | * only version 2 as published by the Free Software Foundation. | ||
| 7 | * | ||
| 8 | * This program is distributed in the hope that it will be useful, | ||
| 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 11 | * GNU General Public License for more details. | ||
| 12 | */ | 4 | */ |
| 13 | 5 | ||
| 14 | #include <linux/amba/bus.h> | 6 | #include <linux/amba/bus.h> |
diff --git a/drivers/hwtracing/coresight/coresight-etb10.c b/drivers/hwtracing/coresight/coresight-etb10.c index 580cd381adf3..9b6c55523c58 100644 --- a/drivers/hwtracing/coresight/coresight-etb10.c +++ b/drivers/hwtracing/coresight/coresight-etb10.c | |||
| @@ -1,15 +1,8 @@ | |||
| 1 | /* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved. | 1 | // SPDX-License-Identifier: GPL-2.0 |
| 2 | /* | ||
| 3 | * Copyright (c) 2011-2012, The Linux Foundation. All rights reserved. | ||
| 2 | * | 4 | * |
| 3 | * Description: CoreSight Embedded Trace Buffer driver | 5 | * Description: CoreSight Embedded Trace Buffer driver |
| 4 | * | ||
| 5 | * This program is free software; you can redistribute it and/or modify | ||
| 6 | * it under the terms of the GNU General Public License version 2 and | ||
| 7 | * only version 2 as published by the Free Software Foundation. | ||
| 8 | * | ||
| 9 | * This program is distributed in the hope that it will be useful, | ||
| 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 12 | * GNU General Public License for more details. | ||
| 13 | */ | 6 | */ |
| 14 | 7 | ||
| 15 | #include <asm/local.h> | 8 | #include <asm/local.h> |
diff --git a/drivers/hwtracing/coresight/coresight-etm-cp14.c b/drivers/hwtracing/coresight/coresight-etm-cp14.c index 12a220682117..4174a8d355d2 100644 --- a/drivers/hwtracing/coresight/coresight-etm-cp14.c +++ b/drivers/hwtracing/coresight/coresight-etm-cp14.c | |||
| @@ -1,13 +1,6 @@ | |||
| 1 | /* Copyright (c) 2012, The Linux Foundation. All rights reserved. | 1 | // SPDX-License-Identifier: GPL-2.0 |
| 2 | * | 2 | /* |
| 3 | * This program is free software; you can redistribute it and/or modify | 3 | * Copyright (c) 2012, The Linux Foundation. All rights reserved. |
| 4 | * it under the terms of the GNU General Public License version 2 and | ||
| 5 | * only version 2 as published by the Free Software Foundation. | ||
| 6 | * | ||
| 7 | * This program is distributed in the hope that it will be useful, | ||
| 8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 10 | * GNU General Public License for more details. | ||
| 11 | */ | 4 | */ |
| 12 | 5 | ||
| 13 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | 6 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
diff --git a/drivers/hwtracing/coresight/coresight-etm-perf.c b/drivers/hwtracing/coresight/coresight-etm-perf.c index 4e5ed6597f2f..677695635211 100644 --- a/drivers/hwtracing/coresight/coresight-etm-perf.c +++ b/drivers/hwtracing/coresight/coresight-etm-perf.c | |||
| @@ -1,18 +1,7 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 1 | /* | 2 | /* |
| 2 | * Copyright(C) 2015 Linaro Limited. All rights reserved. | 3 | * Copyright(C) 2015 Linaro Limited. All rights reserved. |
| 3 | * Author: Mathieu Poirier <mathieu.poirier@linaro.org> | 4 | * Author: Mathieu Poirier <mathieu.poirier@linaro.org> |
| 4 | * | ||
| 5 | * This program is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 as published by | ||
| 7 | * the Free Software Foundation. | ||
| 8 | * | ||
| 9 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
| 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 12 | * more details. | ||
| 13 | * | ||
| 14 | * You should have received a copy of the GNU General Public License along with | ||
| 15 | * this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 16 | */ | 5 | */ |
| 17 | 6 | ||
| 18 | #include <linux/coresight.h> | 7 | #include <linux/coresight.h> |
diff --git a/drivers/hwtracing/coresight/coresight-etm-perf.h b/drivers/hwtracing/coresight/coresight-etm-perf.h index 3ffc9feb2d64..4197df4faf5e 100644 --- a/drivers/hwtracing/coresight/coresight-etm-perf.h +++ b/drivers/hwtracing/coresight/coresight-etm-perf.h | |||
| @@ -1,18 +1,7 @@ | |||
| 1 | /* SPDX-License-Identifier: GPL-2.0 */ | ||
| 1 | /* | 2 | /* |
| 2 | * Copyright(C) 2015 Linaro Limited. All rights reserved. | 3 | * Copyright(C) 2015 Linaro Limited. All rights reserved. |
| 3 | * Author: Mathieu Poirier <mathieu.poirier@linaro.org> | 4 | * Author: Mathieu Poirier <mathieu.poirier@linaro.org> |
| 4 | * | ||
| 5 | * This program is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 as published by | ||
| 7 | * the Free Software Foundation. | ||
| 8 | * | ||
| 9 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
| 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 12 | * more details. | ||
| 13 | * | ||
| 14 | * You should have received a copy of the GNU General Public License along with | ||
| 15 | * this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 16 | */ | 5 | */ |
| 17 | 6 | ||
| 18 | #ifndef _CORESIGHT_ETM_PERF_H | 7 | #ifndef _CORESIGHT_ETM_PERF_H |
diff --git a/drivers/hwtracing/coresight/coresight-etm.h b/drivers/hwtracing/coresight/coresight-etm.h index 70b0a248c321..e8b4549e30e2 100644 --- a/drivers/hwtracing/coresight/coresight-etm.h +++ b/drivers/hwtracing/coresight/coresight-etm.h | |||
| @@ -1,13 +1,6 @@ | |||
| 1 | /* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved. | 1 | /* SPDX-License-Identifier: GPL-2.0 */ |
| 2 | * | 2 | /* |
| 3 | * This program is free software; you can redistribute it and/or modify | 3 | * Copyright (c) 2014-2015, The Linux Foundation. All rights reserved. |
| 4 | * it under the terms of the GNU General Public License version 2 and | ||
| 5 | * only version 2 as published by the Free Software Foundation. | ||
| 6 | * | ||
| 7 | * This program is distributed in the hope that it will be useful, | ||
| 8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 10 | * GNU General Public License for more details. | ||
| 11 | */ | 4 | */ |
| 12 | 5 | ||
| 13 | #ifndef _CORESIGHT_CORESIGHT_ETM_H | 6 | #ifndef _CORESIGHT_CORESIGHT_ETM_H |
diff --git a/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c b/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c index 6e547ec6fead..9435c1481f61 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c | |||
| @@ -1,18 +1,7 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 1 | /* | 2 | /* |
| 2 | * Copyright(C) 2015 Linaro Limited. All rights reserved. | 3 | * Copyright(C) 2015 Linaro Limited. All rights reserved. |
| 3 | * Author: Mathieu Poirier <mathieu.poirier@linaro.org> | 4 | * Author: Mathieu Poirier <mathieu.poirier@linaro.org> |
| 4 | * | ||
| 5 | * This program is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 as published by | ||
| 7 | * the Free Software Foundation. | ||
| 8 | * | ||
| 9 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
| 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 12 | * more details. | ||
| 13 | * | ||
| 14 | * You should have received a copy of the GNU General Public License along with | ||
| 15 | * this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 16 | */ | 5 | */ |
| 17 | 6 | ||
| 18 | #include <linux/pm_runtime.h> | 7 | #include <linux/pm_runtime.h> |
diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c index 39f42fdd503d..15ed64d51a5b 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x.c +++ b/drivers/hwtracing/coresight/coresight-etm3x.c | |||
| @@ -1,15 +1,8 @@ | |||
| 1 | /* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved. | 1 | // SPDX-License-Identifier: GPL-2.0 |
| 2 | /* | ||
| 3 | * Copyright (c) 2011-2012, The Linux Foundation. All rights reserved. | ||
| 2 | * | 4 | * |
| 3 | * Description: CoreSight Program Flow Trace driver | 5 | * Description: CoreSight Program Flow Trace driver |
| 4 | * | ||
| 5 | * This program is free software; you can redistribute it and/or modify | ||
| 6 | * it under the terms of the GNU General Public License version 2 and | ||
| 7 | * only version 2 as published by the Free Software Foundation. | ||
| 8 | * | ||
| 9 | * This program is distributed in the hope that it will be useful, | ||
| 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 12 | * GNU General Public License for more details. | ||
| 13 | */ | 6 | */ |
| 14 | 7 | ||
| 15 | #include <linux/kernel.h> | 8 | #include <linux/kernel.h> |
diff --git a/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c b/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c index d21961710713..4eb8da785ce0 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c | |||
| @@ -1,18 +1,7 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 1 | /* | 2 | /* |
| 2 | * Copyright(C) 2015 Linaro Limited. All rights reserved. | 3 | * Copyright(C) 2015 Linaro Limited. All rights reserved. |
| 3 | * Author: Mathieu Poirier <mathieu.poirier@linaro.org> | 4 | * Author: Mathieu Poirier <mathieu.poirier@linaro.org> |
| 4 | * | ||
| 5 | * This program is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 as published by | ||
| 7 | * the Free Software Foundation. | ||
| 8 | * | ||
| 9 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
| 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 12 | * more details. | ||
| 13 | * | ||
| 14 | * You should have received a copy of the GNU General Public License along with | ||
| 15 | * this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 16 | */ | 5 | */ |
| 17 | 6 | ||
| 18 | #include <linux/pm_runtime.h> | 7 | #include <linux/pm_runtime.h> |
diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c index cf364a514c12..9bc04c50d45b 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.c +++ b/drivers/hwtracing/coresight/coresight-etm4x.c | |||
| @@ -1,13 +1,6 @@ | |||
| 1 | /* Copyright (c) 2014, The Linux Foundation. All rights reserved. | 1 | // SPDX-License-Identifier: GPL-2.0 |
| 2 | * | 2 | /* |
| 3 | * This program is free software; you can redistribute it and/or modify | 3 | * Copyright (c) 2014, The Linux Foundation. All rights reserved. |
| 4 | * it under the terms of the GNU General Public License version 2 and | ||
| 5 | * only version 2 as published by the Free Software Foundation. | ||
| 6 | * | ||
| 7 | * This program is distributed in the hope that it will be useful, | ||
| 8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 10 | * GNU General Public License for more details. | ||
| 11 | */ | 4 | */ |
| 12 | 5 | ||
| 13 | #include <linux/kernel.h> | 6 | #include <linux/kernel.h> |
diff --git a/drivers/hwtracing/coresight/coresight-etm4x.h b/drivers/hwtracing/coresight/coresight-etm4x.h index b3b5ea7b7fb3..b7c4a6f6c6b9 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.h +++ b/drivers/hwtracing/coresight/coresight-etm4x.h | |||
| @@ -1,13 +1,6 @@ | |||
| 1 | /* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved. | 1 | /* SPDX-License-Identifier: GPL-2.0 */ |
| 2 | * | 2 | /* |
| 3 | * This program is free software; you can redistribute it and/or modify | 3 | * Copyright (c) 2014-2015, The Linux Foundation. All rights reserved. |
| 4 | * it under the terms of the GNU General Public License version 2 and | ||
| 5 | * only version 2 as published by the Free Software Foundation. | ||
| 6 | * | ||
| 7 | * This program is distributed in the hope that it will be useful, | ||
| 8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 10 | * GNU General Public License for more details. | ||
| 11 | */ | 4 | */ |
| 12 | 5 | ||
| 13 | #ifndef _CORESIGHT_CORESIGHT_ETM_H | 6 | #ifndef _CORESIGHT_CORESIGHT_ETM_H |
diff --git a/drivers/hwtracing/coresight/coresight-funnel.c b/drivers/hwtracing/coresight/coresight-funnel.c index 9f8ac0bef853..448145a36675 100644 --- a/drivers/hwtracing/coresight/coresight-funnel.c +++ b/drivers/hwtracing/coresight/coresight-funnel.c | |||
| @@ -1,15 +1,8 @@ | |||
| 1 | /* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved. | 1 | // SPDX-License-Identifier: GPL-2.0 |
| 2 | /* | ||
| 3 | * Copyright (c) 2011-2012, The Linux Foundation. All rights reserved. | ||
| 2 | * | 4 | * |
| 3 | * Description: CoreSight Funnel driver | 5 | * Description: CoreSight Funnel driver |
| 4 | * | ||
| 5 | * This program is free software; you can redistribute it and/or modify | ||
| 6 | * it under the terms of the GNU General Public License version 2 and | ||
| 7 | * only version 2 as published by the Free Software Foundation. | ||
| 8 | * | ||
| 9 | * This program is distributed in the hope that it will be useful, | ||
| 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 12 | * GNU General Public License for more details. | ||
| 13 | */ | 6 | */ |
| 14 | 7 | ||
| 15 | #include <linux/kernel.h> | 8 | #include <linux/kernel.h> |
diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index f1d0e21d8cab..0e5a74dae6a6 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h | |||
| @@ -1,13 +1,6 @@ | |||
| 1 | /* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved. | 1 | /* SPDX-License-Identifier: GPL-2.0 */ |
| 2 | * | 2 | /* |
| 3 | * This program is free software; you can redistribute it and/or modify | 3 | * Copyright (c) 2011-2012, The Linux Foundation. All rights reserved. |
| 4 | * it under the terms of the GNU General Public License version 2 and | ||
| 5 | * only version 2 as published by the Free Software Foundation. | ||
| 6 | * | ||
| 7 | * This program is distributed in the hope that it will be useful, | ||
| 8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 10 | * GNU General Public License for more details. | ||
| 11 | */ | 4 | */ |
| 12 | 5 | ||
| 13 | #ifndef _CORESIGHT_PRIV_H | 6 | #ifndef _CORESIGHT_PRIV_H |
diff --git a/drivers/hwtracing/coresight/coresight-replicator.c b/drivers/hwtracing/coresight/coresight-replicator.c index 3756e71cb8f5..8d2eaaab6c2f 100644 --- a/drivers/hwtracing/coresight/coresight-replicator.c +++ b/drivers/hwtracing/coresight/coresight-replicator.c | |||
| @@ -1,15 +1,8 @@ | |||
| 1 | /* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved. | 1 | // SPDX-License-Identifier: GPL-2.0 |
| 2 | /* | ||
| 3 | * Copyright (c) 2011-2012, The Linux Foundation. All rights reserved. | ||
| 2 | * | 4 | * |
| 3 | * Description: CoreSight Replicator driver | 5 | * Description: CoreSight Replicator driver |
| 4 | * | ||
| 5 | * This program is free software; you can redistribute it and/or modify | ||
| 6 | * it under the terms of the GNU General Public License version 2 and | ||
| 7 | * only version 2 as published by the Free Software Foundation. | ||
| 8 | * | ||
| 9 | * This program is distributed in the hope that it will be useful, | ||
| 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 12 | * GNU General Public License for more details. | ||
| 13 | */ | 6 | */ |
| 14 | 7 | ||
| 15 | #include <linux/kernel.h> | 8 | #include <linux/kernel.h> |
diff --git a/drivers/hwtracing/coresight/coresight-stm.c b/drivers/hwtracing/coresight/coresight-stm.c index 15e7ef3891f5..c46c70aec1d5 100644 --- a/drivers/hwtracing/coresight/coresight-stm.c +++ b/drivers/hwtracing/coresight/coresight-stm.c | |||
| @@ -1,16 +1,9 @@ | |||
| 1 | /* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. | 1 | // SPDX-License-Identifier: GPL-2.0 |
| 2 | /* | ||
| 3 | * Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. | ||
| 2 | * | 4 | * |
| 3 | * Description: CoreSight System Trace Macrocell driver | 5 | * Description: CoreSight System Trace Macrocell driver |
| 4 | * | 6 | * |
| 5 | * This program is free software; you can redistribute it and/or modify | ||
| 6 | * it under the terms of the GNU General Public License version 2 and | ||
| 7 | * only version 2 as published by the Free Software Foundation. | ||
| 8 | * | ||
| 9 | * This program is distributed in the hope that it will be useful, | ||
| 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 12 | * GNU General Public License for more details. | ||
| 13 | * | ||
| 14 | * Initial implementation by Pratik Patel | 7 | * Initial implementation by Pratik Patel |
| 15 | * (C) 2014-2015 Pratik Patel <pratikp@codeaurora.org> | 8 | * (C) 2014-2015 Pratik Patel <pratikp@codeaurora.org> |
| 16 | * | 9 | * |
diff --git a/drivers/hwtracing/coresight/coresight-tmc-etf.c b/drivers/hwtracing/coresight/coresight-tmc-etf.c index e2513b786242..61d849b11c26 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etf.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etf.c | |||
| @@ -1,18 +1,7 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 1 | /* | 2 | /* |
| 2 | * Copyright(C) 2016 Linaro Limited. All rights reserved. | 3 | * Copyright(C) 2016 Linaro Limited. All rights reserved. |
| 3 | * Author: Mathieu Poirier <mathieu.poirier@linaro.org> | 4 | * Author: Mathieu Poirier <mathieu.poirier@linaro.org> |
| 4 | * | ||
| 5 | * This program is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 as published by | ||
| 7 | * the Free Software Foundation. | ||
| 8 | * | ||
| 9 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
| 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 12 | * more details. | ||
| 13 | * | ||
| 14 | * You should have received a copy of the GNU General Public License along with | ||
| 15 | * this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 16 | */ | 5 | */ |
| 17 | 6 | ||
| 18 | #include <linux/circ_buf.h> | 7 | #include <linux/circ_buf.h> |
diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c index 68fbc8f7450e..02f747afa2ba 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etr.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c | |||
| @@ -1,18 +1,7 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 1 | /* | 2 | /* |
| 2 | * Copyright(C) 2016 Linaro Limited. All rights reserved. | 3 | * Copyright(C) 2016 Linaro Limited. All rights reserved. |
| 3 | * Author: Mathieu Poirier <mathieu.poirier@linaro.org> | 4 | * Author: Mathieu Poirier <mathieu.poirier@linaro.org> |
| 4 | * | ||
| 5 | * This program is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 as published by | ||
| 7 | * the Free Software Foundation. | ||
| 8 | * | ||
| 9 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
| 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 12 | * more details. | ||
| 13 | * | ||
| 14 | * You should have received a copy of the GNU General Public License along with | ||
| 15 | * this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 16 | */ | 5 | */ |
| 17 | 6 | ||
| 18 | #include <linux/coresight.h> | 7 | #include <linux/coresight.h> |
| @@ -124,10 +113,9 @@ static int tmc_enable_etr_sink_sysfs(struct coresight_device *csdev) | |||
| 124 | bool used = false; | 113 | bool used = false; |
| 125 | unsigned long flags; | 114 | unsigned long flags; |
| 126 | void __iomem *vaddr = NULL; | 115 | void __iomem *vaddr = NULL; |
| 127 | dma_addr_t paddr; | 116 | dma_addr_t paddr = 0; |
| 128 | struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); | 117 | struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); |
| 129 | 118 | ||
| 130 | |||
| 131 | /* | 119 | /* |
| 132 | * If we don't have a buffer release the lock and allocate memory. | 120 | * If we don't have a buffer release the lock and allocate memory. |
| 133 | * Otherwise keep the lock and move along. | 121 | * Otherwise keep the lock and move along. |
| @@ -164,11 +152,11 @@ static int tmc_enable_etr_sink_sysfs(struct coresight_device *csdev) | |||
| 164 | goto out; | 152 | goto out; |
| 165 | 153 | ||
| 166 | /* | 154 | /* |
| 167 | * If drvdata::buf == NULL, use the memory allocated above. | 155 | * If drvdata::vaddr == NULL, use the memory allocated above. |
| 168 | * Otherwise a buffer still exists from a previous session, so | 156 | * Otherwise a buffer still exists from a previous session, so |
| 169 | * simply use that. | 157 | * simply use that. |
| 170 | */ | 158 | */ |
| 171 | if (drvdata->buf == NULL) { | 159 | if (drvdata->vaddr == NULL) { |
| 172 | used = true; | 160 | used = true; |
| 173 | drvdata->vaddr = vaddr; | 161 | drvdata->vaddr = vaddr; |
| 174 | drvdata->paddr = paddr; | 162 | drvdata->paddr = paddr; |
diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c index 0ea04f588de0..456f122df74f 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.c +++ b/drivers/hwtracing/coresight/coresight-tmc.c | |||
| @@ -1,15 +1,7 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 1 | /* Copyright (c) 2012, The Linux Foundation. All rights reserved. | 2 | /* Copyright (c) 2012, The Linux Foundation. All rights reserved. |
| 2 | * | 3 | * |
| 3 | * Description: CoreSight Trace Memory Controller driver | 4 | * Description: CoreSight Trace Memory Controller driver |
| 4 | * | ||
| 5 | * This program is free software; you can redistribute it and/or modify | ||
| 6 | * it under the terms of the GNU General Public License version 2 and | ||
| 7 | * only version 2 as published by the Free Software Foundation. | ||
| 8 | * | ||
| 9 | * This program is distributed in the hope that it will be useful, | ||
| 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 12 | * GNU General Public License for more details. | ||
| 13 | */ | 5 | */ |
| 14 | 6 | ||
| 15 | #include <linux/kernel.h> | 7 | #include <linux/kernel.h> |
diff --git a/drivers/hwtracing/coresight/coresight-tmc.h b/drivers/hwtracing/coresight/coresight-tmc.h index 8df7a813f537..dfaff077a7fc 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.h +++ b/drivers/hwtracing/coresight/coresight-tmc.h | |||
| @@ -1,18 +1,7 @@ | |||
| 1 | /* SPDX-License-Identifier: GPL-2.0 */ | ||
| 1 | /* | 2 | /* |
| 2 | * Copyright(C) 2015 Linaro Limited. All rights reserved. | 3 | * Copyright(C) 2015 Linaro Limited. All rights reserved. |
| 3 | * Author: Mathieu Poirier <mathieu.poirier@linaro.org> | 4 | * Author: Mathieu Poirier <mathieu.poirier@linaro.org> |
| 4 | * | ||
| 5 | * This program is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 as published by | ||
| 7 | * the Free Software Foundation. | ||
| 8 | * | ||
| 9 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
| 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 12 | * more details. | ||
| 13 | * | ||
| 14 | * You should have received a copy of the GNU General Public License along with | ||
| 15 | * this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 16 | */ | 5 | */ |
| 17 | 6 | ||
| 18 | #ifndef _CORESIGHT_TMC_H | 7 | #ifndef _CORESIGHT_TMC_H |
diff --git a/drivers/hwtracing/coresight/coresight-tpiu.c b/drivers/hwtracing/coresight/coresight-tpiu.c index 805f7c2210fe..01b7457fe8fc 100644 --- a/drivers/hwtracing/coresight/coresight-tpiu.c +++ b/drivers/hwtracing/coresight/coresight-tpiu.c | |||
| @@ -1,15 +1,8 @@ | |||
| 1 | /* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved. | 1 | // SPDX-License-Identifier: GPL-2.0 |
| 2 | /* | ||
| 3 | * Copyright (c) 2011-2012, The Linux Foundation. All rights reserved. | ||
| 2 | * | 4 | * |
| 3 | * Description: CoreSight Trace Port Interface Unit driver | 5 | * Description: CoreSight Trace Port Interface Unit driver |
| 4 | * | ||
| 5 | * This program is free software; you can redistribute it and/or modify | ||
| 6 | * it under the terms of the GNU General Public License version 2 and | ||
| 7 | * only version 2 as published by the Free Software Foundation. | ||
| 8 | * | ||
| 9 | * This program is distributed in the hope that it will be useful, | ||
| 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 12 | * GNU General Public License for more details. | ||
| 13 | */ | 6 | */ |
| 14 | 7 | ||
| 15 | #include <linux/kernel.h> | 8 | #include <linux/kernel.h> |
diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c index 389c4baeca9d..29e834aab539 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c | |||
| @@ -1,13 +1,6 @@ | |||
| 1 | /* Copyright (c) 2012, The Linux Foundation. All rights reserved. | 1 | // SPDX-License-Identifier: GPL-2.0 |
| 2 | * | 2 | /* |
| 3 | * This program is free software; you can redistribute it and/or modify | 3 | * Copyright (c) 2012, The Linux Foundation. All rights reserved. |
| 4 | * it under the terms of the GNU General Public License version 2 and | ||
| 5 | * only version 2 as published by the Free Software Foundation. | ||
| 6 | * | ||
| 7 | * This program is distributed in the hope that it will be useful, | ||
| 8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 10 | * GNU General Public License for more details. | ||
| 11 | */ | 4 | */ |
| 12 | 5 | ||
| 13 | #include <linux/kernel.h> | 6 | #include <linux/kernel.h> |
| @@ -1026,8 +1019,10 @@ struct coresight_device *coresight_register(struct coresight_desc *desc) | |||
| 1026 | dev_set_name(&csdev->dev, "%s", desc->pdata->name); | 1019 | dev_set_name(&csdev->dev, "%s", desc->pdata->name); |
| 1027 | 1020 | ||
| 1028 | ret = device_register(&csdev->dev); | 1021 | ret = device_register(&csdev->dev); |
| 1029 | if (ret) | 1022 | if (ret) { |
| 1030 | goto err_device_register; | 1023 | put_device(&csdev->dev); |
| 1024 | goto err_kzalloc_csdev; | ||
| 1025 | } | ||
| 1031 | 1026 | ||
| 1032 | mutex_lock(&coresight_mutex); | 1027 | mutex_lock(&coresight_mutex); |
| 1033 | 1028 | ||
| @@ -1038,8 +1033,6 @@ struct coresight_device *coresight_register(struct coresight_desc *desc) | |||
| 1038 | 1033 | ||
| 1039 | return csdev; | 1034 | return csdev; |
| 1040 | 1035 | ||
| 1041 | err_device_register: | ||
| 1042 | kfree(conns); | ||
| 1043 | err_kzalloc_conns: | 1036 | err_kzalloc_conns: |
| 1044 | kfree(refcnts); | 1037 | kfree(refcnts); |
| 1045 | err_kzalloc_refcnts: | 1038 | err_kzalloc_refcnts: |
diff --git a/drivers/hwtracing/coresight/of_coresight.c b/drivers/hwtracing/coresight/of_coresight.c index 7c375443ede6..a33a92ebe74b 100644 --- a/drivers/hwtracing/coresight/of_coresight.c +++ b/drivers/hwtracing/coresight/of_coresight.c | |||
| @@ -1,13 +1,6 @@ | |||
| 1 | /* Copyright (c) 2012, The Linux Foundation. All rights reserved. | 1 | // SPDX-License-Identifier: GPL-2.0 |
| 2 | * | 2 | /* |
| 3 | * This program is free software; you can redistribute it and/or modify | 3 | * Copyright (c) 2012, The Linux Foundation. All rights reserved. |
| 4 | * it under the terms of the GNU General Public License version 2 and | ||
| 5 | * only version 2 as published by the Free Software Foundation. | ||
| 6 | * | ||
| 7 | * This program is distributed in the hope that it will be useful, | ||
| 8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 10 | * GNU General Public License for more details. | ||
| 11 | */ | 4 | */ |
| 12 | 5 | ||
| 13 | #include <linux/types.h> | 6 | #include <linux/types.h> |
diff --git a/drivers/hwtracing/stm/ftrace.c b/drivers/hwtracing/stm/ftrace.c index 7da75644c750..ce868e095410 100644 --- a/drivers/hwtracing/stm/ftrace.c +++ b/drivers/hwtracing/stm/ftrace.c | |||
| @@ -1,16 +1,8 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 1 | /* | 2 | /* |
| 2 | * Simple kernel driver to link kernel Ftrace and an STM device | 3 | * Simple kernel driver to link kernel Ftrace and an STM device |
| 3 | * Copyright (c) 2016, Linaro Ltd. | 4 | * Copyright (c) 2016, Linaro Ltd. |
| 4 | * | 5 | * |
| 5 | * This program is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms and conditions of the GNU General Public License, | ||
| 7 | * version 2, as published by the Free Software Foundation. | ||
| 8 | * | ||
| 9 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
| 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 12 | * more details. | ||
| 13 | * | ||
| 14 | * STM Ftrace will be registered as a trace_export. | 6 | * STM Ftrace will be registered as a trace_export. |
| 15 | */ | 7 | */ |
| 16 | 8 | ||
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 5d713008749b..3726eacdf65d 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig | |||
| @@ -113,6 +113,20 @@ config IBM_ASM | |||
| 113 | for information on the specific driver level and support statement | 113 | for information on the specific driver level and support statement |
| 114 | for your IBM server. | 114 | for your IBM server. |
| 115 | 115 | ||
| 116 | config IBMVMC | ||
| 117 | tristate "IBM Virtual Management Channel support" | ||
| 118 | depends on PPC_PSERIES | ||
| 119 | help | ||
| 120 | This is the IBM POWER Virtual Management Channel | ||
| 121 | |||
| 122 | This driver is to be used for the POWER Virtual | ||
| 123 | Management Channel virtual adapter on the PowerVM | ||
| 124 | platform. It provides both request/response and | ||
| 125 | async message support through the /dev/ibmvmc node. | ||
| 126 | |||
| 127 | To compile this driver as a module, choose M here: the | ||
| 128 | module will be called ibmvmc. | ||
| 129 | |||
| 116 | config PHANTOM | 130 | config PHANTOM |
| 117 | tristate "Sensable PHANToM (PCI)" | 131 | tristate "Sensable PHANToM (PCI)" |
| 118 | depends on PCI | 132 | depends on PCI |
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 20be70c3f118..af22bbc3d00c 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | # | 4 | # |
| 5 | 5 | ||
| 6 | obj-$(CONFIG_IBM_ASM) += ibmasm/ | 6 | obj-$(CONFIG_IBM_ASM) += ibmasm/ |
| 7 | obj-$(CONFIG_IBMVMC) += ibmvmc.o | ||
| 7 | obj-$(CONFIG_AD525X_DPOT) += ad525x_dpot.o | 8 | obj-$(CONFIG_AD525X_DPOT) += ad525x_dpot.o |
| 8 | obj-$(CONFIG_AD525X_DPOT_I2C) += ad525x_dpot-i2c.o | 9 | obj-$(CONFIG_AD525X_DPOT_I2C) += ad525x_dpot-i2c.o |
| 9 | obj-$(CONFIG_AD525X_DPOT_SPI) += ad525x_dpot-spi.o | 10 | obj-$(CONFIG_AD525X_DPOT_SPI) += ad525x_dpot-spi.o |
diff --git a/drivers/misc/cxl/context.c b/drivers/misc/cxl/context.c index 7ff315ad3692..c6ec872800a2 100644 --- a/drivers/misc/cxl/context.c +++ b/drivers/misc/cxl/context.c | |||
| @@ -128,11 +128,12 @@ void cxl_context_set_mapping(struct cxl_context *ctx, | |||
| 128 | mutex_unlock(&ctx->mapping_lock); | 128 | mutex_unlock(&ctx->mapping_lock); |
| 129 | } | 129 | } |
| 130 | 130 | ||
| 131 | static int cxl_mmap_fault(struct vm_fault *vmf) | 131 | static vm_fault_t cxl_mmap_fault(struct vm_fault *vmf) |
| 132 | { | 132 | { |
| 133 | struct vm_area_struct *vma = vmf->vma; | 133 | struct vm_area_struct *vma = vmf->vma; |
| 134 | struct cxl_context *ctx = vma->vm_file->private_data; | 134 | struct cxl_context *ctx = vma->vm_file->private_data; |
| 135 | u64 area, offset; | 135 | u64 area, offset; |
| 136 | vm_fault_t ret; | ||
| 136 | 137 | ||
| 137 | offset = vmf->pgoff << PAGE_SHIFT; | 138 | offset = vmf->pgoff << PAGE_SHIFT; |
| 138 | 139 | ||
| @@ -169,11 +170,11 @@ static int cxl_mmap_fault(struct vm_fault *vmf) | |||
| 169 | return VM_FAULT_SIGBUS; | 170 | return VM_FAULT_SIGBUS; |
| 170 | } | 171 | } |
| 171 | 172 | ||
| 172 | vm_insert_pfn(vma, vmf->address, (area + offset) >> PAGE_SHIFT); | 173 | ret = vmf_insert_pfn(vma, vmf->address, (area + offset) >> PAGE_SHIFT); |
| 173 | 174 | ||
| 174 | mutex_unlock(&ctx->status_mutex); | 175 | mutex_unlock(&ctx->status_mutex); |
| 175 | 176 | ||
| 176 | return VM_FAULT_NOPAGE; | 177 | return ret; |
| 177 | } | 178 | } |
| 178 | 179 | ||
| 179 | static const struct vm_operations_struct cxl_mmap_vmops = { | 180 | static const struct vm_operations_struct cxl_mmap_vmops = { |
diff --git a/drivers/misc/ibmvmc.c b/drivers/misc/ibmvmc.c new file mode 100644 index 000000000000..fb83d1375638 --- /dev/null +++ b/drivers/misc/ibmvmc.c | |||
| @@ -0,0 +1,2418 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0+ | ||
| 2 | /* | ||
| 3 | * IBM Power Systems Virtual Management Channel Support. | ||
| 4 | * | ||
| 5 | * Copyright (c) 2004, 2018 IBM Corp. | ||
| 6 | * Dave Engebretsen engebret@us.ibm.com | ||
| 7 | * Steven Royer seroyer@linux.vnet.ibm.com | ||
| 8 | * Adam Reznechek adreznec@linux.vnet.ibm.com | ||
| 9 | * Bryant G. Ly <bryantly@linux.vnet.ibm.com> | ||
| 10 | */ | ||
| 11 | |||
| 12 | #include <linux/module.h> | ||
| 13 | #include <linux/kernel.h> | ||
| 14 | #include <linux/kthread.h> | ||
| 15 | #include <linux/major.h> | ||
| 16 | #include <linux/string.h> | ||
| 17 | #include <linux/fcntl.h> | ||
| 18 | #include <linux/slab.h> | ||
| 19 | #include <linux/poll.h> | ||
| 20 | #include <linux/init.h> | ||
| 21 | #include <linux/fs.h> | ||
| 22 | #include <linux/interrupt.h> | ||
| 23 | #include <linux/spinlock.h> | ||
| 24 | #include <linux/percpu.h> | ||
| 25 | #include <linux/delay.h> | ||
| 26 | #include <linux/uaccess.h> | ||
| 27 | #include <linux/io.h> | ||
| 28 | #include <linux/miscdevice.h> | ||
| 29 | #include <linux/sched/signal.h> | ||
| 30 | |||
| 31 | #include <asm/byteorder.h> | ||
| 32 | #include <asm/irq.h> | ||
| 33 | #include <asm/vio.h> | ||
| 34 | |||
| 35 | #include "ibmvmc.h" | ||
| 36 | |||
| 37 | #define IBMVMC_DRIVER_VERSION "1.0" | ||
| 38 | |||
| 39 | /* | ||
| 40 | * Static global variables | ||
| 41 | */ | ||
| 42 | static DECLARE_WAIT_QUEUE_HEAD(ibmvmc_read_wait); | ||
| 43 | |||
| 44 | static const char ibmvmc_driver_name[] = "ibmvmc"; | ||
| 45 | |||
| 46 | static struct ibmvmc_struct ibmvmc; | ||
| 47 | static struct ibmvmc_hmc hmcs[MAX_HMCS]; | ||
| 48 | static struct crq_server_adapter ibmvmc_adapter; | ||
| 49 | |||
| 50 | static int ibmvmc_max_buf_pool_size = DEFAULT_BUF_POOL_SIZE; | ||
| 51 | static int ibmvmc_max_hmcs = DEFAULT_HMCS; | ||
| 52 | static int ibmvmc_max_mtu = DEFAULT_MTU; | ||
| 53 | |||
| 54 | static inline long h_copy_rdma(s64 length, u64 sliobn, u64 slioba, | ||
| 55 | u64 dliobn, u64 dlioba) | ||
| 56 | { | ||
| 57 | long rc = 0; | ||
| 58 | |||
| 59 | /* Ensure all writes to source memory are visible before hcall */ | ||
| 60 | dma_wmb(); | ||
| 61 | pr_debug("ibmvmc: h_copy_rdma(0x%llx, 0x%llx, 0x%llx, 0x%llx, 0x%llx\n", | ||
| 62 | length, sliobn, slioba, dliobn, dlioba); | ||
| 63 | rc = plpar_hcall_norets(H_COPY_RDMA, length, sliobn, slioba, | ||
| 64 | dliobn, dlioba); | ||
| 65 | pr_debug("ibmvmc: h_copy_rdma rc = 0x%lx\n", rc); | ||
| 66 | |||
| 67 | return rc; | ||
| 68 | } | ||
| 69 | |||
| 70 | static inline void h_free_crq(uint32_t unit_address) | ||
| 71 | { | ||
| 72 | long rc = 0; | ||
| 73 | |||
| 74 | do { | ||
| 75 | if (H_IS_LONG_BUSY(rc)) | ||
| 76 | msleep(get_longbusy_msecs(rc)); | ||
| 77 | |||
| 78 | rc = plpar_hcall_norets(H_FREE_CRQ, unit_address); | ||
| 79 | } while ((rc == H_BUSY) || (H_IS_LONG_BUSY(rc))); | ||
| 80 | } | ||
| 81 | |||
| 82 | /** | ||
| 83 | * h_request_vmc: - request a hypervisor virtual management channel device | ||
| 84 | * @vmc_index: drc index of the vmc device created | ||
| 85 | * | ||
| 86 | * Requests the hypervisor create a new virtual management channel device, | ||
| 87 | * allowing this partition to send hypervisor virtualization control | ||
| 88 | * commands. | ||
| 89 | * | ||
| 90 | * Return: | ||
| 91 | * 0 - Success | ||
| 92 | * Non-zero - Failure | ||
| 93 | */ | ||
| 94 | static inline long h_request_vmc(u32 *vmc_index) | ||
| 95 | { | ||
| 96 | long rc = 0; | ||
| 97 | unsigned long retbuf[PLPAR_HCALL_BUFSIZE]; | ||
| 98 | |||
| 99 | do { | ||
| 100 | if (H_IS_LONG_BUSY(rc)) | ||
| 101 | msleep(get_longbusy_msecs(rc)); | ||
| 102 | |||
| 103 | /* Call to request the VMC device from phyp */ | ||
| 104 | rc = plpar_hcall(H_REQUEST_VMC, retbuf); | ||
| 105 | pr_debug("ibmvmc: %s rc = 0x%lx\n", __func__, rc); | ||
| 106 | *vmc_index = retbuf[0]; | ||
| 107 | } while ((rc == H_BUSY) || (H_IS_LONG_BUSY(rc))); | ||
| 108 | |||
| 109 | return rc; | ||
| 110 | } | ||
| 111 | |||
| 112 | /* routines for managing a command/response queue */ | ||
| 113 | /** | ||
| 114 | * ibmvmc_handle_event: - Interrupt handler for crq events | ||
| 115 | * @irq: number of irq to handle, not used | ||
| 116 | * @dev_instance: crq_server_adapter that received interrupt | ||
| 117 | * | ||
| 118 | * Disables interrupts and schedules ibmvmc_task | ||
| 119 | * | ||
| 120 | * Always returns IRQ_HANDLED | ||
| 121 | */ | ||
| 122 | static irqreturn_t ibmvmc_handle_event(int irq, void *dev_instance) | ||
| 123 | { | ||
| 124 | struct crq_server_adapter *adapter = | ||
| 125 | (struct crq_server_adapter *)dev_instance; | ||
| 126 | |||
| 127 | vio_disable_interrupts(to_vio_dev(adapter->dev)); | ||
| 128 | tasklet_schedule(&adapter->work_task); | ||
| 129 | |||
| 130 | return IRQ_HANDLED; | ||
| 131 | } | ||
| 132 | |||
| 133 | /** | ||
| 134 | * ibmvmc_release_crq_queue - Release CRQ Queue | ||
| 135 | * | ||
| 136 | * @adapter: crq_server_adapter struct | ||
| 137 | * | ||
| 138 | * Return: | ||
| 139 | * 0 - Success | ||
| 140 | * Non-Zero - Failure | ||
| 141 | */ | ||
| 142 | static void ibmvmc_release_crq_queue(struct crq_server_adapter *adapter) | ||
| 143 | { | ||
| 144 | struct vio_dev *vdev = to_vio_dev(adapter->dev); | ||
| 145 | struct crq_queue *queue = &adapter->queue; | ||
| 146 | |||
| 147 | free_irq(vdev->irq, (void *)adapter); | ||
| 148 | tasklet_kill(&adapter->work_task); | ||
| 149 | |||
| 150 | if (adapter->reset_task) | ||
| 151 | kthread_stop(adapter->reset_task); | ||
| 152 | |||
| 153 | h_free_crq(vdev->unit_address); | ||
| 154 | dma_unmap_single(adapter->dev, | ||
| 155 | queue->msg_token, | ||
| 156 | queue->size * sizeof(*queue->msgs), DMA_BIDIRECTIONAL); | ||
| 157 | free_page((unsigned long)queue->msgs); | ||
| 158 | } | ||
| 159 | |||
| 160 | /** | ||
| 161 | * ibmvmc_reset_crq_queue - Reset CRQ Queue | ||
| 162 | * | ||
| 163 | * @adapter: crq_server_adapter struct | ||
| 164 | * | ||
| 165 | * This function calls h_free_crq and then calls H_REG_CRQ and does all the | ||
| 166 | * bookkeeping to get us back to where we can communicate. | ||
| 167 | * | ||
| 168 | * Return: | ||
| 169 | * 0 - Success | ||
| 170 | * Non-Zero - Failure | ||
| 171 | */ | ||
| 172 | static int ibmvmc_reset_crq_queue(struct crq_server_adapter *adapter) | ||
| 173 | { | ||
| 174 | struct vio_dev *vdev = to_vio_dev(adapter->dev); | ||
| 175 | struct crq_queue *queue = &adapter->queue; | ||
| 176 | int rc = 0; | ||
| 177 | |||
| 178 | /* Close the CRQ */ | ||
| 179 | h_free_crq(vdev->unit_address); | ||
| 180 | |||
| 181 | /* Clean out the queue */ | ||
| 182 | memset(queue->msgs, 0x00, PAGE_SIZE); | ||
| 183 | queue->cur = 0; | ||
| 184 | |||
| 185 | /* And re-open it again */ | ||
| 186 | rc = plpar_hcall_norets(H_REG_CRQ, | ||
| 187 | vdev->unit_address, | ||
| 188 | queue->msg_token, PAGE_SIZE); | ||
| 189 | if (rc == 2) | ||
| 190 | /* Adapter is good, but other end is not ready */ | ||
| 191 | dev_warn(adapter->dev, "Partner adapter not ready\n"); | ||
| 192 | else if (rc != 0) | ||
| 193 | dev_err(adapter->dev, "couldn't register crq--rc 0x%x\n", rc); | ||
| 194 | |||
| 195 | return rc; | ||
| 196 | } | ||
| 197 | |||
| 198 | /** | ||
| 199 | * crq_queue_next_crq: - Returns the next entry in message queue | ||
| 200 | * @queue: crq_queue to use | ||
| 201 | * | ||
| 202 | * Returns pointer to next entry in queue, or NULL if there are no new | ||
| 203 | * entried in the CRQ. | ||
| 204 | */ | ||
| 205 | static struct ibmvmc_crq_msg *crq_queue_next_crq(struct crq_queue *queue) | ||
| 206 | { | ||
| 207 | struct ibmvmc_crq_msg *crq; | ||
| 208 | unsigned long flags; | ||
| 209 | |||
| 210 | spin_lock_irqsave(&queue->lock, flags); | ||
| 211 | crq = &queue->msgs[queue->cur]; | ||
| 212 | if (crq->valid & 0x80) { | ||
| 213 | if (++queue->cur == queue->size) | ||
| 214 | queue->cur = 0; | ||
| 215 | |||
| 216 | /* Ensure the read of the valid bit occurs before reading any | ||
| 217 | * other bits of the CRQ entry | ||
| 218 | */ | ||
| 219 | dma_rmb(); | ||
| 220 | } else { | ||
| 221 | crq = NULL; | ||
| 222 | } | ||
| 223 | |||
| 224 | spin_unlock_irqrestore(&queue->lock, flags); | ||
| 225 | |||
| 226 | return crq; | ||
| 227 | } | ||
| 228 | |||
| 229 | /** | ||
| 230 | * ibmvmc_send_crq - Send CRQ | ||
| 231 | * | ||
| 232 | * @adapter: crq_server_adapter struct | ||
| 233 | * @word1: Word1 Data field | ||
| 234 | * @word2: Word2 Data field | ||
| 235 | * | ||
| 236 | * Return: | ||
| 237 | * 0 - Success | ||
| 238 | * Non-Zero - Failure | ||
| 239 | */ | ||
| 240 | static long ibmvmc_send_crq(struct crq_server_adapter *adapter, | ||
| 241 | u64 word1, u64 word2) | ||
| 242 | { | ||
| 243 | struct vio_dev *vdev = to_vio_dev(adapter->dev); | ||
| 244 | long rc = 0; | ||
| 245 | |||
| 246 | dev_dbg(adapter->dev, "(0x%x, 0x%016llx, 0x%016llx)\n", | ||
| 247 | vdev->unit_address, word1, word2); | ||
| 248 | |||
| 249 | /* | ||
| 250 | * Ensure the command buffer is flushed to memory before handing it | ||
| 251 | * over to the other side to prevent it from fetching any stale data. | ||
| 252 | */ | ||
| 253 | dma_wmb(); | ||
| 254 | rc = plpar_hcall_norets(H_SEND_CRQ, vdev->unit_address, word1, word2); | ||
| 255 | dev_dbg(adapter->dev, "rc = 0x%lx\n", rc); | ||
| 256 | |||
| 257 | return rc; | ||
| 258 | } | ||
| 259 | |||
| 260 | /** | ||
| 261 | * alloc_dma_buffer - Create DMA Buffer | ||
| 262 | * | ||
| 263 | * @vdev: vio_dev struct | ||
| 264 | * @size: Size field | ||
| 265 | * @dma_handle: DMA address field | ||
| 266 | * | ||
| 267 | * Allocates memory for the command queue and maps remote memory into an | ||
| 268 | * ioba. | ||
| 269 | * | ||
| 270 | * Returns a pointer to the buffer | ||
| 271 | */ | ||
| 272 | static void *alloc_dma_buffer(struct vio_dev *vdev, size_t size, | ||
| 273 | dma_addr_t *dma_handle) | ||
| 274 | { | ||
| 275 | /* allocate memory */ | ||
| 276 | void *buffer = kzalloc(size, GFP_KERNEL); | ||
| 277 | |||
| 278 | if (!buffer) { | ||
| 279 | *dma_handle = 0; | ||
| 280 | return NULL; | ||
| 281 | } | ||
| 282 | |||
| 283 | /* DMA map */ | ||
| 284 | *dma_handle = dma_map_single(&vdev->dev, buffer, size, | ||
| 285 | DMA_BIDIRECTIONAL); | ||
| 286 | |||
| 287 | if (dma_mapping_error(&vdev->dev, *dma_handle)) { | ||
| 288 | *dma_handle = 0; | ||
| 289 | kzfree(buffer); | ||
| 290 | return NULL; | ||
| 291 | } | ||
| 292 | |||
| 293 | return buffer; | ||
| 294 | } | ||
| 295 | |||
| 296 | /** | ||
| 297 | * free_dma_buffer - Free DMA Buffer | ||
| 298 | * | ||
| 299 | * @vdev: vio_dev struct | ||
| 300 | * @size: Size field | ||
| 301 | * @vaddr: Address field | ||
| 302 | * @dma_handle: DMA address field | ||
| 303 | * | ||
| 304 | * Releases memory for a command queue and unmaps mapped remote memory. | ||
| 305 | */ | ||
| 306 | static void free_dma_buffer(struct vio_dev *vdev, size_t size, void *vaddr, | ||
| 307 | dma_addr_t dma_handle) | ||
| 308 | { | ||
| 309 | /* DMA unmap */ | ||
| 310 | dma_unmap_single(&vdev->dev, dma_handle, size, DMA_BIDIRECTIONAL); | ||
| 311 | |||
| 312 | /* deallocate memory */ | ||
| 313 | kzfree(vaddr); | ||
| 314 | } | ||
| 315 | |||
| 316 | /** | ||
| 317 | * ibmvmc_get_valid_hmc_buffer - Retrieve Valid HMC Buffer | ||
| 318 | * | ||
| 319 | * @hmc_index: HMC Index Field | ||
| 320 | * | ||
| 321 | * Return: | ||
| 322 | * Pointer to ibmvmc_buffer | ||
| 323 | */ | ||
| 324 | static struct ibmvmc_buffer *ibmvmc_get_valid_hmc_buffer(u8 hmc_index) | ||
| 325 | { | ||
| 326 | struct ibmvmc_buffer *buffer; | ||
| 327 | struct ibmvmc_buffer *ret_buf = NULL; | ||
| 328 | unsigned long i; | ||
| 329 | |||
| 330 | if (hmc_index > ibmvmc.max_hmc_index) | ||
| 331 | return NULL; | ||
| 332 | |||
| 333 | buffer = hmcs[hmc_index].buffer; | ||
| 334 | |||
| 335 | for (i = 0; i < ibmvmc_max_buf_pool_size; i++) { | ||
| 336 | if (buffer[i].valid && buffer[i].free && | ||
| 337 | buffer[i].owner == VMC_BUF_OWNER_ALPHA) { | ||
| 338 | buffer[i].free = 0; | ||
| 339 | ret_buf = &buffer[i]; | ||
| 340 | break; | ||
| 341 | } | ||
| 342 | } | ||
| 343 | |||
| 344 | return ret_buf; | ||
| 345 | } | ||
| 346 | |||
| 347 | /** | ||
| 348 | * ibmvmc_get_free_hmc_buffer - Get Free HMC Buffer | ||
| 349 | * | ||
| 350 | * @adapter: crq_server_adapter struct | ||
| 351 | * @hmc_index: Hmc Index field | ||
| 352 | * | ||
| 353 | * Return: | ||
| 354 | * Pointer to ibmvmc_buffer | ||
| 355 | */ | ||
| 356 | static struct ibmvmc_buffer *ibmvmc_get_free_hmc_buffer(struct crq_server_adapter *adapter, | ||
| 357 | u8 hmc_index) | ||
| 358 | { | ||
| 359 | struct ibmvmc_buffer *buffer; | ||
| 360 | struct ibmvmc_buffer *ret_buf = NULL; | ||
| 361 | unsigned long i; | ||
| 362 | |||
| 363 | if (hmc_index > ibmvmc.max_hmc_index) { | ||
| 364 | dev_info(adapter->dev, "get_free_hmc_buffer: invalid hmc_index=0x%x\n", | ||
| 365 | hmc_index); | ||
| 366 | return NULL; | ||
| 367 | } | ||
| 368 | |||
| 369 | buffer = hmcs[hmc_index].buffer; | ||
| 370 | |||
| 371 | for (i = 0; i < ibmvmc_max_buf_pool_size; i++) { | ||
| 372 | if (buffer[i].free && | ||
| 373 | buffer[i].owner == VMC_BUF_OWNER_ALPHA) { | ||
| 374 | buffer[i].free = 0; | ||
| 375 | ret_buf = &buffer[i]; | ||
| 376 | break; | ||
| 377 | } | ||
| 378 | } | ||
| 379 | |||
| 380 | return ret_buf; | ||
| 381 | } | ||
| 382 | |||
| 383 | /** | ||
| 384 | * ibmvmc_free_hmc_buffer - Free an HMC Buffer | ||
| 385 | * | ||
| 386 | * @hmc: ibmvmc_hmc struct | ||
| 387 | * @buffer: ibmvmc_buffer struct | ||
| 388 | * | ||
| 389 | */ | ||
| 390 | static void ibmvmc_free_hmc_buffer(struct ibmvmc_hmc *hmc, | ||
| 391 | struct ibmvmc_buffer *buffer) | ||
| 392 | { | ||
| 393 | unsigned long flags; | ||
| 394 | |||
| 395 | spin_lock_irqsave(&hmc->lock, flags); | ||
| 396 | buffer->free = 1; | ||
| 397 | spin_unlock_irqrestore(&hmc->lock, flags); | ||
| 398 | } | ||
| 399 | |||
| 400 | /** | ||
| 401 | * ibmvmc_count_hmc_buffers - Count HMC Buffers | ||
| 402 | * | ||
| 403 | * @hmc_index: HMC Index field | ||
| 404 | * @valid: Valid number of buffers field | ||
| 405 | * @free: Free number of buffers field | ||
| 406 | * | ||
| 407 | */ | ||
| 408 | static void ibmvmc_count_hmc_buffers(u8 hmc_index, unsigned int *valid, | ||
| 409 | unsigned int *free) | ||
| 410 | { | ||
| 411 | struct ibmvmc_buffer *buffer; | ||
| 412 | unsigned long i; | ||
| 413 | unsigned long flags; | ||
| 414 | |||
| 415 | if (hmc_index > ibmvmc.max_hmc_index) | ||
| 416 | return; | ||
| 417 | |||
| 418 | if (!valid || !free) | ||
| 419 | return; | ||
| 420 | |||
| 421 | *valid = 0; *free = 0; | ||
| 422 | |||
| 423 | buffer = hmcs[hmc_index].buffer; | ||
| 424 | spin_lock_irqsave(&hmcs[hmc_index].lock, flags); | ||
| 425 | |||
| 426 | for (i = 0; i < ibmvmc_max_buf_pool_size; i++) { | ||
| 427 | if (buffer[i].valid) { | ||
| 428 | *valid = *valid + 1; | ||
| 429 | if (buffer[i].free) | ||
| 430 | *free = *free + 1; | ||
| 431 | } | ||
| 432 | } | ||
| 433 | |||
| 434 | spin_unlock_irqrestore(&hmcs[hmc_index].lock, flags); | ||
| 435 | } | ||
| 436 | |||
| 437 | /** | ||
| 438 | * ibmvmc_get_free_hmc - Get Free HMC | ||
| 439 | * | ||
| 440 | * Return: | ||
| 441 | * Pointer to an available HMC Connection | ||
| 442 | * Null otherwise | ||
| 443 | */ | ||
| 444 | static struct ibmvmc_hmc *ibmvmc_get_free_hmc(void) | ||
| 445 | { | ||
| 446 | unsigned long i; | ||
| 447 | unsigned long flags; | ||
| 448 | |||
| 449 | /* | ||
| 450 | * Find an available HMC connection. | ||
| 451 | */ | ||
| 452 | for (i = 0; i <= ibmvmc.max_hmc_index; i++) { | ||
| 453 | spin_lock_irqsave(&hmcs[i].lock, flags); | ||
| 454 | if (hmcs[i].state == ibmhmc_state_free) { | ||
| 455 | hmcs[i].index = i; | ||
| 456 | hmcs[i].state = ibmhmc_state_initial; | ||
| 457 | spin_unlock_irqrestore(&hmcs[i].lock, flags); | ||
| 458 | return &hmcs[i]; | ||
| 459 | } | ||
| 460 | spin_unlock_irqrestore(&hmcs[i].lock, flags); | ||
| 461 | } | ||
| 462 | |||
| 463 | return NULL; | ||
| 464 | } | ||
| 465 | |||
| 466 | /** | ||
| 467 | * ibmvmc_return_hmc - Return an HMC Connection | ||
| 468 | * | ||
| 469 | * @hmc: ibmvmc_hmc struct | ||
| 470 | * @release_readers: Number of readers connected to session | ||
| 471 | * | ||
| 472 | * This function releases the HMC connections back into the pool. | ||
| 473 | * | ||
| 474 | * Return: | ||
| 475 | * 0 - Success | ||
| 476 | * Non-zero - Failure | ||
| 477 | */ | ||
| 478 | static int ibmvmc_return_hmc(struct ibmvmc_hmc *hmc, bool release_readers) | ||
| 479 | { | ||
| 480 | struct ibmvmc_buffer *buffer; | ||
| 481 | struct crq_server_adapter *adapter; | ||
| 482 | struct vio_dev *vdev; | ||
| 483 | unsigned long i; | ||
| 484 | unsigned long flags; | ||
| 485 | |||
| 486 | if (!hmc || !hmc->adapter) | ||
| 487 | return -EIO; | ||
| 488 | |||
| 489 | if (release_readers) { | ||
| 490 | if (hmc->file_session) { | ||
| 491 | struct ibmvmc_file_session *session = hmc->file_session; | ||
| 492 | |||
| 493 | session->valid = 0; | ||
| 494 | wake_up_interruptible(&ibmvmc_read_wait); | ||
| 495 | } | ||
| 496 | } | ||
| 497 | |||
| 498 | adapter = hmc->adapter; | ||
| 499 | vdev = to_vio_dev(adapter->dev); | ||
| 500 | |||
| 501 | spin_lock_irqsave(&hmc->lock, flags); | ||
| 502 | hmc->index = 0; | ||
| 503 | hmc->state = ibmhmc_state_free; | ||
| 504 | hmc->queue_head = 0; | ||
| 505 | hmc->queue_tail = 0; | ||
| 506 | buffer = hmc->buffer; | ||
| 507 | for (i = 0; i < ibmvmc_max_buf_pool_size; i++) { | ||
| 508 | if (buffer[i].valid) { | ||
| 509 | free_dma_buffer(vdev, | ||
| 510 | ibmvmc.max_mtu, | ||
| 511 | buffer[i].real_addr_local, | ||
| 512 | buffer[i].dma_addr_local); | ||
| 513 | dev_dbg(adapter->dev, "Forgot buffer id 0x%lx\n", i); | ||
| 514 | } | ||
| 515 | memset(&buffer[i], 0, sizeof(struct ibmvmc_buffer)); | ||
| 516 | |||
| 517 | hmc->queue_outbound_msgs[i] = VMC_INVALID_BUFFER_ID; | ||
| 518 | } | ||
| 519 | |||
| 520 | spin_unlock_irqrestore(&hmc->lock, flags); | ||
| 521 | |||
| 522 | return 0; | ||
| 523 | } | ||
| 524 | |||
| 525 | /** | ||
| 526 | * ibmvmc_send_open - Interface Open | ||
| 527 | * @buffer: Pointer to ibmvmc_buffer struct | ||
| 528 | * @hmc: Pointer to ibmvmc_hmc struct | ||
| 529 | * | ||
| 530 | * This command is sent by the management partition as the result of a | ||
| 531 | * management partition device request. It causes the hypervisor to | ||
| 532 | * prepare a set of data buffers for the management application connection | ||
| 533 | * indicated HMC idx. A unique HMC Idx would be used if multiple management | ||
| 534 | * applications running concurrently were desired. Before responding to this | ||
| 535 | * command, the hypervisor must provide the management partition with at | ||
| 536 | * least one of these new buffers via the Add Buffer. This indicates whether | ||
| 537 | * the messages are inbound or outbound from the hypervisor. | ||
| 538 | * | ||
| 539 | * Return: | ||
| 540 | * 0 - Success | ||
| 541 | * Non-zero - Failure | ||
| 542 | */ | ||
| 543 | static int ibmvmc_send_open(struct ibmvmc_buffer *buffer, | ||
| 544 | struct ibmvmc_hmc *hmc) | ||
| 545 | { | ||
| 546 | struct ibmvmc_crq_msg crq_msg; | ||
| 547 | struct crq_server_adapter *adapter; | ||
| 548 | __be64 *crq_as_u64 = (__be64 *)&crq_msg; | ||
| 549 | int rc = 0; | ||
| 550 | |||
| 551 | if (!hmc || !hmc->adapter) | ||
| 552 | return -EIO; | ||
| 553 | |||
| 554 | adapter = hmc->adapter; | ||
| 555 | |||
| 556 | dev_dbg(adapter->dev, "send_open: 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx\n", | ||
| 557 | (unsigned long)buffer->size, (unsigned long)adapter->liobn, | ||
| 558 | (unsigned long)buffer->dma_addr_local, | ||
| 559 | (unsigned long)adapter->riobn, | ||
| 560 | (unsigned long)buffer->dma_addr_remote); | ||
| 561 | |||
| 562 | rc = h_copy_rdma(buffer->size, | ||
| 563 | adapter->liobn, | ||
| 564 | buffer->dma_addr_local, | ||
| 565 | adapter->riobn, | ||
| 566 | buffer->dma_addr_remote); | ||
| 567 | if (rc) { | ||
| 568 | dev_err(adapter->dev, "Error: In send_open, h_copy_rdma rc 0x%x\n", | ||
| 569 | rc); | ||
| 570 | return -EIO; | ||
| 571 | } | ||
| 572 | |||
| 573 | hmc->state = ibmhmc_state_opening; | ||
| 574 | |||
| 575 | crq_msg.valid = 0x80; | ||
| 576 | crq_msg.type = VMC_MSG_OPEN; | ||
| 577 | crq_msg.status = 0; | ||
| 578 | crq_msg.var1.rsvd = 0; | ||
| 579 | crq_msg.hmc_session = hmc->session; | ||
| 580 | crq_msg.hmc_index = hmc->index; | ||
| 581 | crq_msg.var2.buffer_id = cpu_to_be16(buffer->id); | ||
| 582 | crq_msg.rsvd = 0; | ||
| 583 | crq_msg.var3.rsvd = 0; | ||
| 584 | |||
| 585 | ibmvmc_send_crq(adapter, be64_to_cpu(crq_as_u64[0]), | ||
| 586 | be64_to_cpu(crq_as_u64[1])); | ||
| 587 | |||
| 588 | return rc; | ||
| 589 | } | ||
| 590 | |||
| 591 | /** | ||
| 592 | * ibmvmc_send_close - Interface Close | ||
| 593 | * @hmc: Pointer to ibmvmc_hmc struct | ||
| 594 | * | ||
| 595 | * This command is sent by the management partition to terminate a | ||
| 596 | * management application to hypervisor connection. When this command is | ||
| 597 | * sent, the management partition has quiesced all I/O operations to all | ||
| 598 | * buffers associated with this management application connection, and | ||
| 599 | * has freed any storage for these buffers. | ||
| 600 | * | ||
| 601 | * Return: | ||
| 602 | * 0 - Success | ||
| 603 | * Non-zero - Failure | ||
| 604 | */ | ||
| 605 | static int ibmvmc_send_close(struct ibmvmc_hmc *hmc) | ||
| 606 | { | ||
| 607 | struct ibmvmc_crq_msg crq_msg; | ||
| 608 | struct crq_server_adapter *adapter; | ||
| 609 | __be64 *crq_as_u64 = (__be64 *)&crq_msg; | ||
| 610 | int rc = 0; | ||
| 611 | |||
| 612 | if (!hmc || !hmc->adapter) | ||
| 613 | return -EIO; | ||
| 614 | |||
| 615 | adapter = hmc->adapter; | ||
| 616 | |||
| 617 | dev_info(adapter->dev, "CRQ send: close\n"); | ||
| 618 | |||
| 619 | crq_msg.valid = 0x80; | ||
| 620 | crq_msg.type = VMC_MSG_CLOSE; | ||
| 621 | crq_msg.status = 0; | ||
| 622 | crq_msg.var1.rsvd = 0; | ||
| 623 | crq_msg.hmc_session = hmc->session; | ||
| 624 | crq_msg.hmc_index = hmc->index; | ||
| 625 | crq_msg.var2.rsvd = 0; | ||
| 626 | crq_msg.rsvd = 0; | ||
| 627 | crq_msg.var3.rsvd = 0; | ||
| 628 | |||
| 629 | ibmvmc_send_crq(adapter, be64_to_cpu(crq_as_u64[0]), | ||
| 630 | be64_to_cpu(crq_as_u64[1])); | ||
| 631 | |||
| 632 | return rc; | ||
| 633 | } | ||
| 634 | |||
| 635 | /** | ||
| 636 | * ibmvmc_send_capabilities - Send VMC Capabilities | ||
| 637 | * | ||
| 638 | * @adapter: crq_server_adapter struct | ||
| 639 | * | ||
| 640 | * The capabilities message is an administrative message sent after the CRQ | ||
| 641 | * initialization sequence of messages and is used to exchange VMC capabilities | ||
| 642 | * between the management partition and the hypervisor. The management | ||
| 643 | * partition must send this message and the hypervisor must respond with VMC | ||
| 644 | * capabilities Response message before HMC interface message can begin. Any | ||
| 645 | * HMC interface messages received before the exchange of capabilities has | ||
| 646 | * complete are dropped. | ||
| 647 | * | ||
| 648 | * Return: | ||
| 649 | * 0 - Success | ||
| 650 | */ | ||
| 651 | static int ibmvmc_send_capabilities(struct crq_server_adapter *adapter) | ||
| 652 | { | ||
| 653 | struct ibmvmc_admin_crq_msg crq_msg; | ||
| 654 | __be64 *crq_as_u64 = (__be64 *)&crq_msg; | ||
| 655 | |||
| 656 | dev_dbg(adapter->dev, "ibmvmc: CRQ send: capabilities\n"); | ||
| 657 | crq_msg.valid = 0x80; | ||
| 658 | crq_msg.type = VMC_MSG_CAP; | ||
| 659 | crq_msg.status = 0; | ||
| 660 | crq_msg.rsvd[0] = 0; | ||
| 661 | crq_msg.rsvd[1] = 0; | ||
| 662 | crq_msg.max_hmc = ibmvmc_max_hmcs; | ||
| 663 | crq_msg.max_mtu = cpu_to_be32(ibmvmc_max_mtu); | ||
| 664 | crq_msg.pool_size = cpu_to_be16(ibmvmc_max_buf_pool_size); | ||
| 665 | crq_msg.crq_size = cpu_to_be16(adapter->queue.size); | ||
| 666 | crq_msg.version = cpu_to_be16(IBMVMC_PROTOCOL_VERSION); | ||
| 667 | |||
| 668 | ibmvmc_send_crq(adapter, be64_to_cpu(crq_as_u64[0]), | ||
| 669 | be64_to_cpu(crq_as_u64[1])); | ||
| 670 | |||
| 671 | ibmvmc.state = ibmvmc_state_capabilities; | ||
| 672 | |||
| 673 | return 0; | ||
| 674 | } | ||
| 675 | |||
| 676 | /** | ||
| 677 | * ibmvmc_send_add_buffer_resp - Add Buffer Response | ||
| 678 | * | ||
| 679 | * @adapter: crq_server_adapter struct | ||
| 680 | * @status: Status field | ||
| 681 | * @hmc_session: HMC Session field | ||
| 682 | * @hmc_index: HMC Index field | ||
| 683 | * @buffer_id: Buffer Id field | ||
| 684 | * | ||
| 685 | * This command is sent by the management partition to the hypervisor in | ||
| 686 | * response to the Add Buffer message. The Status field indicates the result of | ||
| 687 | * the command. | ||
| 688 | * | ||
| 689 | * Return: | ||
| 690 | * 0 - Success | ||
| 691 | */ | ||
| 692 | static int ibmvmc_send_add_buffer_resp(struct crq_server_adapter *adapter, | ||
| 693 | u8 status, u8 hmc_session, | ||
| 694 | u8 hmc_index, u16 buffer_id) | ||
| 695 | { | ||
| 696 | struct ibmvmc_crq_msg crq_msg; | ||
| 697 | __be64 *crq_as_u64 = (__be64 *)&crq_msg; | ||
| 698 | |||
| 699 | dev_dbg(adapter->dev, "CRQ send: add_buffer_resp\n"); | ||
| 700 | crq_msg.valid = 0x80; | ||
| 701 | crq_msg.type = VMC_MSG_ADD_BUF_RESP; | ||
| 702 | crq_msg.status = status; | ||
| 703 | crq_msg.var1.rsvd = 0; | ||
| 704 | crq_msg.hmc_session = hmc_session; | ||
| 705 | crq_msg.hmc_index = hmc_index; | ||
| 706 | crq_msg.var2.buffer_id = cpu_to_be16(buffer_id); | ||
| 707 | crq_msg.rsvd = 0; | ||
| 708 | crq_msg.var3.rsvd = 0; | ||
| 709 | |||
| 710 | ibmvmc_send_crq(adapter, be64_to_cpu(crq_as_u64[0]), | ||
| 711 | be64_to_cpu(crq_as_u64[1])); | ||
| 712 | |||
| 713 | return 0; | ||
| 714 | } | ||
| 715 | |||
| 716 | /** | ||
| 717 | * ibmvmc_send_rem_buffer_resp - Remove Buffer Response | ||
| 718 | * | ||
| 719 | * @adapter: crq_server_adapter struct | ||
| 720 | * @status: Status field | ||
| 721 | * @hmc_session: HMC Session field | ||
| 722 | * @hmc_index: HMC Index field | ||
| 723 | * @buffer_id: Buffer Id field | ||
| 724 | * | ||
| 725 | * This command is sent by the management partition to the hypervisor in | ||
| 726 | * response to the Remove Buffer message. The Buffer ID field indicates | ||
| 727 | * which buffer the management partition selected to remove. The Status | ||
| 728 | * field indicates the result of the command. | ||
| 729 | * | ||
| 730 | * Return: | ||
| 731 | * 0 - Success | ||
| 732 | */ | ||
| 733 | static int ibmvmc_send_rem_buffer_resp(struct crq_server_adapter *adapter, | ||
| 734 | u8 status, u8 hmc_session, | ||
| 735 | u8 hmc_index, u16 buffer_id) | ||
| 736 | { | ||
| 737 | struct ibmvmc_crq_msg crq_msg; | ||
| 738 | __be64 *crq_as_u64 = (__be64 *)&crq_msg; | ||
| 739 | |||
| 740 | dev_dbg(adapter->dev, "CRQ send: rem_buffer_resp\n"); | ||
| 741 | crq_msg.valid = 0x80; | ||
| 742 | crq_msg.type = VMC_MSG_REM_BUF_RESP; | ||
| 743 | crq_msg.status = status; | ||
| 744 | crq_msg.var1.rsvd = 0; | ||
| 745 | crq_msg.hmc_session = hmc_session; | ||
| 746 | crq_msg.hmc_index = hmc_index; | ||
| 747 | crq_msg.var2.buffer_id = cpu_to_be16(buffer_id); | ||
| 748 | crq_msg.rsvd = 0; | ||
| 749 | crq_msg.var3.rsvd = 0; | ||
| 750 | |||
| 751 | ibmvmc_send_crq(adapter, be64_to_cpu(crq_as_u64[0]), | ||
| 752 | be64_to_cpu(crq_as_u64[1])); | ||
| 753 | |||
| 754 | return 0; | ||
| 755 | } | ||
| 756 | |||
| 757 | /** | ||
| 758 | * ibmvmc_send_msg - Signal Message | ||
| 759 | * | ||
| 760 | * @adapter: crq_server_adapter struct | ||
| 761 | * @buffer: ibmvmc_buffer struct | ||
| 762 | * @hmc: ibmvmc_hmc struct | ||
| 763 | * @msg_length: message length field | ||
| 764 | * | ||
| 765 | * This command is sent between the management partition and the hypervisor | ||
| 766 | * in order to signal the arrival of an HMC protocol message. The command | ||
| 767 | * can be sent by both the management partition and the hypervisor. It is | ||
| 768 | * used for all traffic between the management application and the hypervisor, | ||
| 769 | * regardless of who initiated the communication. | ||
| 770 | * | ||
| 771 | * There is no response to this message. | ||
| 772 | * | ||
| 773 | * Return: | ||
| 774 | * 0 - Success | ||
| 775 | * Non-zero - Failure | ||
| 776 | */ | ||
| 777 | static int ibmvmc_send_msg(struct crq_server_adapter *adapter, | ||
| 778 | struct ibmvmc_buffer *buffer, | ||
| 779 | struct ibmvmc_hmc *hmc, int msg_len) | ||
| 780 | { | ||
| 781 | struct ibmvmc_crq_msg crq_msg; | ||
| 782 | __be64 *crq_as_u64 = (__be64 *)&crq_msg; | ||
| 783 | int rc = 0; | ||
| 784 | |||
| 785 | dev_dbg(adapter->dev, "CRQ send: rdma to HV\n"); | ||
| 786 | rc = h_copy_rdma(msg_len, | ||
| 787 | adapter->liobn, | ||
| 788 | buffer->dma_addr_local, | ||
| 789 | adapter->riobn, | ||
| 790 | buffer->dma_addr_remote); | ||
| 791 | if (rc) { | ||
| 792 | dev_err(adapter->dev, "Error in send_msg, h_copy_rdma rc 0x%x\n", | ||
| 793 | rc); | ||
| 794 | return rc; | ||
| 795 | } | ||
| 796 | |||
| 797 | crq_msg.valid = 0x80; | ||
| 798 | crq_msg.type = VMC_MSG_SIGNAL; | ||
| 799 | crq_msg.status = 0; | ||
| 800 | crq_msg.var1.rsvd = 0; | ||
| 801 | crq_msg.hmc_session = hmc->session; | ||
| 802 | crq_msg.hmc_index = hmc->index; | ||
| 803 | crq_msg.var2.buffer_id = cpu_to_be16(buffer->id); | ||
| 804 | crq_msg.var3.msg_len = cpu_to_be32(msg_len); | ||
| 805 | dev_dbg(adapter->dev, "CRQ send: msg to HV 0x%llx 0x%llx\n", | ||
| 806 | be64_to_cpu(crq_as_u64[0]), be64_to_cpu(crq_as_u64[1])); | ||
| 807 | |||
| 808 | buffer->owner = VMC_BUF_OWNER_HV; | ||
| 809 | ibmvmc_send_crq(adapter, be64_to_cpu(crq_as_u64[0]), | ||
| 810 | be64_to_cpu(crq_as_u64[1])); | ||
| 811 | |||
| 812 | return rc; | ||
| 813 | } | ||
| 814 | |||
| 815 | /** | ||
| 816 | * ibmvmc_open - Open Session | ||
| 817 | * | ||
| 818 | * @inode: inode struct | ||
| 819 | * @file: file struct | ||
| 820 | * | ||
| 821 | * Return: | ||
| 822 | * 0 - Success | ||
| 823 | */ | ||
| 824 | static int ibmvmc_open(struct inode *inode, struct file *file) | ||
| 825 | { | ||
| 826 | struct ibmvmc_file_session *session; | ||
| 827 | int rc = 0; | ||
| 828 | |||
| 829 | pr_debug("%s: inode = 0x%lx, file = 0x%lx, state = 0x%x\n", __func__, | ||
| 830 | (unsigned long)inode, (unsigned long)file, | ||
| 831 | ibmvmc.state); | ||
| 832 | |||
| 833 | session = kzalloc(sizeof(*session), GFP_KERNEL); | ||
| 834 | session->file = file; | ||
| 835 | file->private_data = session; | ||
| 836 | |||
| 837 | return rc; | ||
| 838 | } | ||
| 839 | |||
| 840 | /** | ||
| 841 | * ibmvmc_close - Close Session | ||
| 842 | * | ||
| 843 | * @inode: inode struct | ||
| 844 | * @file: file struct | ||
| 845 | * | ||
| 846 | * Return: | ||
| 847 | * 0 - Success | ||
| 848 | * Non-zero - Failure | ||
| 849 | */ | ||
| 850 | static int ibmvmc_close(struct inode *inode, struct file *file) | ||
| 851 | { | ||
| 852 | struct ibmvmc_file_session *session; | ||
| 853 | struct ibmvmc_hmc *hmc; | ||
| 854 | int rc = 0; | ||
| 855 | unsigned long flags; | ||
| 856 | |||
| 857 | pr_debug("%s: file = 0x%lx, state = 0x%x\n", __func__, | ||
| 858 | (unsigned long)file, ibmvmc.state); | ||
| 859 | |||
| 860 | session = file->private_data; | ||
| 861 | if (!session) | ||
| 862 | return -EIO; | ||
| 863 | |||
| 864 | hmc = session->hmc; | ||
| 865 | if (hmc) { | ||
| 866 | if (!hmc->adapter) | ||
| 867 | return -EIO; | ||
| 868 | |||
| 869 | if (ibmvmc.state == ibmvmc_state_failed) { | ||
| 870 | dev_warn(hmc->adapter->dev, "close: state_failed\n"); | ||
| 871 | return -EIO; | ||
| 872 | } | ||
| 873 | |||
| 874 | spin_lock_irqsave(&hmc->lock, flags); | ||
| 875 | if (hmc->state >= ibmhmc_state_opening) { | ||
| 876 | rc = ibmvmc_send_close(hmc); | ||
| 877 | if (rc) | ||
| 878 | dev_warn(hmc->adapter->dev, "close: send_close failed.\n"); | ||
| 879 | } | ||
| 880 | spin_unlock_irqrestore(&hmc->lock, flags); | ||
| 881 | } | ||
| 882 | |||
| 883 | kzfree(session); | ||
| 884 | |||
| 885 | return rc; | ||
| 886 | } | ||
| 887 | |||
| 888 | /** | ||
| 889 | * ibmvmc_read - Read | ||
| 890 | * | ||
| 891 | * @file: file struct | ||
| 892 | * @buf: Character buffer | ||
| 893 | * @nbytes: Size in bytes | ||
| 894 | * @ppos: Offset | ||
| 895 | * | ||
| 896 | * Return: | ||
| 897 | * 0 - Success | ||
| 898 | * Non-zero - Failure | ||
| 899 | */ | ||
| 900 | static ssize_t ibmvmc_read(struct file *file, char *buf, size_t nbytes, | ||
| 901 | loff_t *ppos) | ||
| 902 | { | ||
| 903 | struct ibmvmc_file_session *session; | ||
| 904 | struct ibmvmc_hmc *hmc; | ||
| 905 | struct crq_server_adapter *adapter; | ||
| 906 | struct ibmvmc_buffer *buffer; | ||
| 907 | ssize_t n; | ||
| 908 | ssize_t retval = 0; | ||
| 909 | unsigned long flags; | ||
| 910 | DEFINE_WAIT(wait); | ||
| 911 | |||
| 912 | pr_debug("ibmvmc: read: file = 0x%lx, buf = 0x%lx, nbytes = 0x%lx\n", | ||
| 913 | (unsigned long)file, (unsigned long)buf, | ||
| 914 | (unsigned long)nbytes); | ||
| 915 | |||
| 916 | if (nbytes == 0) | ||
| 917 | return 0; | ||
| 918 | |||
| 919 | if (nbytes > ibmvmc.max_mtu) { | ||
| 920 | pr_warn("ibmvmc: read: nbytes invalid 0x%x\n", | ||
| 921 | (unsigned int)nbytes); | ||
| 922 | return -EINVAL; | ||
| 923 | } | ||
| 924 | |||
| 925 | session = file->private_data; | ||
| 926 | if (!session) { | ||
| 927 | pr_warn("ibmvmc: read: no session\n"); | ||
| 928 | return -EIO; | ||
| 929 | } | ||
| 930 | |||
| 931 | hmc = session->hmc; | ||
| 932 | if (!hmc) { | ||
| 933 | pr_warn("ibmvmc: read: no hmc\n"); | ||
| 934 | return -EIO; | ||
| 935 | } | ||
| 936 | |||
| 937 | adapter = hmc->adapter; | ||
| 938 | if (!adapter) { | ||
| 939 | pr_warn("ibmvmc: read: no adapter\n"); | ||
| 940 | return -EIO; | ||
| 941 | } | ||
| 942 | |||
| 943 | do { | ||
| 944 | prepare_to_wait(&ibmvmc_read_wait, &wait, TASK_INTERRUPTIBLE); | ||
| 945 | |||
| 946 | spin_lock_irqsave(&hmc->lock, flags); | ||
| 947 | if (hmc->queue_tail != hmc->queue_head) | ||
| 948 | /* Data is available */ | ||
| 949 | break; | ||
| 950 | |||
| 951 | spin_unlock_irqrestore(&hmc->lock, flags); | ||
| 952 | |||
| 953 | if (!session->valid) { | ||
| 954 | retval = -EBADFD; | ||
| 955 | goto out; | ||
| 956 | } | ||
| 957 | if (file->f_flags & O_NONBLOCK) { | ||
| 958 | retval = -EAGAIN; | ||
| 959 | goto out; | ||
| 960 | } | ||
| 961 | |||
| 962 | schedule(); | ||
| 963 | |||
| 964 | if (signal_pending(current)) { | ||
| 965 | retval = -ERESTARTSYS; | ||
| 966 | goto out; | ||
| 967 | } | ||
| 968 | } while (1); | ||
| 969 | |||
| 970 | buffer = &(hmc->buffer[hmc->queue_outbound_msgs[hmc->queue_tail]]); | ||
| 971 | hmc->queue_tail++; | ||
| 972 | if (hmc->queue_tail == ibmvmc_max_buf_pool_size) | ||
| 973 | hmc->queue_tail = 0; | ||
| 974 | spin_unlock_irqrestore(&hmc->lock, flags); | ||
| 975 | |||
| 976 | nbytes = min_t(size_t, nbytes, buffer->msg_len); | ||
| 977 | n = copy_to_user((void *)buf, buffer->real_addr_local, nbytes); | ||
| 978 | dev_dbg(adapter->dev, "read: copy to user nbytes = 0x%lx.\n", nbytes); | ||
| 979 | ibmvmc_free_hmc_buffer(hmc, buffer); | ||
| 980 | retval = nbytes; | ||
| 981 | |||
| 982 | if (n) { | ||
| 983 | dev_warn(adapter->dev, "read: copy to user failed.\n"); | ||
| 984 | retval = -EFAULT; | ||
| 985 | } | ||
| 986 | |||
| 987 | out: | ||
| 988 | finish_wait(&ibmvmc_read_wait, &wait); | ||
| 989 | dev_dbg(adapter->dev, "read: out %ld\n", retval); | ||
| 990 | return retval; | ||
| 991 | } | ||
| 992 | |||
| 993 | /** | ||
| 994 | * ibmvmc_poll - Poll | ||
| 995 | * | ||
| 996 | * @file: file struct | ||
| 997 | * @wait: Poll Table | ||
| 998 | * | ||
| 999 | * Return: | ||
| 1000 | * poll.h return values | ||
| 1001 | */ | ||
| 1002 | static unsigned int ibmvmc_poll(struct file *file, poll_table *wait) | ||
| 1003 | { | ||
| 1004 | struct ibmvmc_file_session *session; | ||
| 1005 | struct ibmvmc_hmc *hmc; | ||
| 1006 | unsigned int mask = 0; | ||
| 1007 | |||
| 1008 | session = file->private_data; | ||
| 1009 | if (!session) | ||
| 1010 | return 0; | ||
| 1011 | |||
| 1012 | hmc = session->hmc; | ||
| 1013 | if (!hmc) | ||
| 1014 | return 0; | ||
| 1015 | |||
| 1016 | poll_wait(file, &ibmvmc_read_wait, wait); | ||
| 1017 | |||
| 1018 | if (hmc->queue_head != hmc->queue_tail) | ||
| 1019 | mask |= POLLIN | POLLRDNORM; | ||
| 1020 | |||
| 1021 | return mask; | ||
| 1022 | } | ||
| 1023 | |||
| 1024 | /** | ||
| 1025 | * ibmvmc_write - Write | ||
| 1026 | * | ||
| 1027 | * @file: file struct | ||
| 1028 | * @buf: Character buffer | ||
| 1029 | * @count: Count field | ||
| 1030 | * @ppos: Offset | ||
| 1031 | * | ||
| 1032 | * Return: | ||
| 1033 | * 0 - Success | ||
| 1034 | * Non-zero - Failure | ||
| 1035 | */ | ||
| 1036 | static ssize_t ibmvmc_write(struct file *file, const char *buffer, | ||
| 1037 | size_t count, loff_t *ppos) | ||
| 1038 | { | ||
| 1039 | struct ibmvmc_buffer *vmc_buffer; | ||
| 1040 | struct ibmvmc_file_session *session; | ||
| 1041 | struct crq_server_adapter *adapter; | ||
| 1042 | struct ibmvmc_hmc *hmc; | ||
| 1043 | unsigned char *buf; | ||
| 1044 | unsigned long flags; | ||
| 1045 | size_t bytes; | ||
| 1046 | const char *p = buffer; | ||
| 1047 | size_t c = count; | ||
| 1048 | int ret = 0; | ||
| 1049 | |||
| 1050 | session = file->private_data; | ||
| 1051 | if (!session) | ||
| 1052 | return -EIO; | ||
| 1053 | |||
| 1054 | hmc = session->hmc; | ||
| 1055 | if (!hmc) | ||
| 1056 | return -EIO; | ||
| 1057 | |||
| 1058 | spin_lock_irqsave(&hmc->lock, flags); | ||
| 1059 | if (hmc->state == ibmhmc_state_free) { | ||
| 1060 | /* HMC connection is not valid (possibly was reset under us). */ | ||
| 1061 | ret = -EIO; | ||
| 1062 | goto out; | ||
| 1063 | } | ||
| 1064 | |||
| 1065 | adapter = hmc->adapter; | ||
| 1066 | if (!adapter) { | ||
| 1067 | ret = -EIO; | ||
| 1068 | goto out; | ||
| 1069 | } | ||
| 1070 | |||
| 1071 | if (count > ibmvmc.max_mtu) { | ||
| 1072 | dev_warn(adapter->dev, "invalid buffer size 0x%lx\n", | ||
| 1073 | (unsigned long)count); | ||
| 1074 | ret = -EIO; | ||
| 1075 | goto out; | ||
| 1076 | } | ||
| 1077 | |||
| 1078 | /* Waiting for the open resp message to the ioctl(1) - retry */ | ||
| 1079 | if (hmc->state == ibmhmc_state_opening) { | ||
| 1080 | ret = -EBUSY; | ||
| 1081 | goto out; | ||
| 1082 | } | ||
| 1083 | |||
| 1084 | /* Make sure the ioctl() was called & the open msg sent, and that | ||
| 1085 | * the HMC connection has not failed. | ||
| 1086 | */ | ||
| 1087 | if (hmc->state != ibmhmc_state_ready) { | ||
| 1088 | ret = -EIO; | ||
| 1089 | goto out; | ||
| 1090 | } | ||
| 1091 | |||
| 1092 | vmc_buffer = ibmvmc_get_valid_hmc_buffer(hmc->index); | ||
| 1093 | if (!vmc_buffer) { | ||
| 1094 | /* No buffer available for the msg send, or we have not yet | ||
| 1095 | * completed the open/open_resp sequence. Retry until this is | ||
| 1096 | * complete. | ||
| 1097 | */ | ||
| 1098 | ret = -EBUSY; | ||
| 1099 | goto out; | ||
| 1100 | } | ||
| 1101 | if (!vmc_buffer->real_addr_local) { | ||
| 1102 | dev_err(adapter->dev, "no buffer storage assigned\n"); | ||
| 1103 | ret = -EIO; | ||
| 1104 | goto out; | ||
| 1105 | } | ||
| 1106 | buf = vmc_buffer->real_addr_local; | ||
| 1107 | |||
| 1108 | while (c > 0) { | ||
| 1109 | bytes = min_t(size_t, c, vmc_buffer->size); | ||
| 1110 | |||
| 1111 | bytes -= copy_from_user(buf, p, bytes); | ||
| 1112 | if (!bytes) { | ||
| 1113 | ret = -EFAULT; | ||
| 1114 | goto out; | ||
| 1115 | } | ||
| 1116 | c -= bytes; | ||
| 1117 | p += bytes; | ||
| 1118 | } | ||
| 1119 | if (p == buffer) | ||
| 1120 | goto out; | ||
| 1121 | |||
| 1122 | file->f_path.dentry->d_inode->i_mtime = current_time(file_inode(file)); | ||
| 1123 | mark_inode_dirty(file->f_path.dentry->d_inode); | ||
| 1124 | |||
| 1125 | dev_dbg(adapter->dev, "write: file = 0x%lx, count = 0x%lx\n", | ||
| 1126 | (unsigned long)file, (unsigned long)count); | ||
| 1127 | |||
| 1128 | ibmvmc_send_msg(adapter, vmc_buffer, hmc, count); | ||
| 1129 | ret = p - buffer; | ||
| 1130 | out: | ||
| 1131 | spin_unlock_irqrestore(&hmc->lock, flags); | ||
| 1132 | return (ssize_t)(ret); | ||
| 1133 | } | ||
| 1134 | |||
| 1135 | /** | ||
| 1136 | * ibmvmc_setup_hmc - Setup the HMC | ||
| 1137 | * | ||
| 1138 | * @session: ibmvmc_file_session struct | ||
| 1139 | * | ||
| 1140 | * Return: | ||
| 1141 | * 0 - Success | ||
| 1142 | * Non-zero - Failure | ||
| 1143 | */ | ||
| 1144 | static long ibmvmc_setup_hmc(struct ibmvmc_file_session *session) | ||
| 1145 | { | ||
| 1146 | struct ibmvmc_hmc *hmc; | ||
| 1147 | unsigned int valid, free, index; | ||
| 1148 | |||
| 1149 | if (ibmvmc.state == ibmvmc_state_failed) { | ||
| 1150 | pr_warn("ibmvmc: Reserve HMC: state_failed\n"); | ||
| 1151 | return -EIO; | ||
| 1152 | } | ||
| 1153 | |||
| 1154 | if (ibmvmc.state < ibmvmc_state_ready) { | ||
| 1155 | pr_warn("ibmvmc: Reserve HMC: not state_ready\n"); | ||
| 1156 | return -EAGAIN; | ||
| 1157 | } | ||
| 1158 | |||
| 1159 | /* Device is busy until capabilities have been exchanged and we | ||
| 1160 | * have a generic buffer for each possible HMC connection. | ||
| 1161 | */ | ||
| 1162 | for (index = 0; index <= ibmvmc.max_hmc_index; index++) { | ||
| 1163 | valid = 0; | ||
| 1164 | ibmvmc_count_hmc_buffers(index, &valid, &free); | ||
| 1165 | if (valid == 0) { | ||
| 1166 | pr_warn("ibmvmc: buffers not ready for index %d\n", | ||
| 1167 | index); | ||
| 1168 | return -ENOBUFS; | ||
| 1169 | } | ||
| 1170 | } | ||
| 1171 | |||
| 1172 | /* Get an hmc object, and transition to ibmhmc_state_initial */ | ||
| 1173 | hmc = ibmvmc_get_free_hmc(); | ||
| 1174 | if (!hmc) { | ||
| 1175 | pr_warn("%s: free hmc not found\n", __func__); | ||
| 1176 | return -EBUSY; | ||
| 1177 | } | ||
| 1178 | |||
| 1179 | hmc->session = hmc->session + 1; | ||
| 1180 | if (hmc->session == 0xff) | ||
| 1181 | hmc->session = 1; | ||
| 1182 | |||
| 1183 | session->hmc = hmc; | ||
| 1184 | hmc->adapter = &ibmvmc_adapter; | ||
| 1185 | hmc->file_session = session; | ||
| 1186 | session->valid = 1; | ||
| 1187 | |||
| 1188 | return 0; | ||
| 1189 | } | ||
| 1190 | |||
| 1191 | /** | ||
| 1192 | * ibmvmc_ioctl_sethmcid - IOCTL Set HMC ID | ||
| 1193 | * | ||
| 1194 | * @session: ibmvmc_file_session struct | ||
| 1195 | * @new_hmc_id: HMC id field | ||
| 1196 | * | ||
| 1197 | * IOCTL command to setup the hmc id | ||
| 1198 | * | ||
| 1199 | * Return: | ||
| 1200 | * 0 - Success | ||
| 1201 | * Non-zero - Failure | ||
| 1202 | */ | ||
| 1203 | static long ibmvmc_ioctl_sethmcid(struct ibmvmc_file_session *session, | ||
| 1204 | unsigned char __user *new_hmc_id) | ||
| 1205 | { | ||
| 1206 | struct ibmvmc_hmc *hmc; | ||
| 1207 | struct ibmvmc_buffer *buffer; | ||
| 1208 | size_t bytes; | ||
| 1209 | char print_buffer[HMC_ID_LEN + 1]; | ||
| 1210 | unsigned long flags; | ||
| 1211 | long rc = 0; | ||
| 1212 | |||
| 1213 | /* Reserve HMC session */ | ||
| 1214 | hmc = session->hmc; | ||
| 1215 | if (!hmc) { | ||
| 1216 | rc = ibmvmc_setup_hmc(session); | ||
| 1217 | if (rc) | ||
| 1218 | return rc; | ||
| 1219 | |||
| 1220 | hmc = session->hmc; | ||
| 1221 | if (!hmc) { | ||
| 1222 | pr_err("ibmvmc: setup_hmc success but no hmc\n"); | ||
| 1223 | return -EIO; | ||
| 1224 | } | ||
| 1225 | } | ||
| 1226 | |||
| 1227 | if (hmc->state != ibmhmc_state_initial) { | ||
| 1228 | pr_warn("ibmvmc: sethmcid: invalid state to send open 0x%x\n", | ||
| 1229 | hmc->state); | ||
| 1230 | return -EIO; | ||
| 1231 | } | ||
| 1232 | |||
| 1233 | bytes = copy_from_user(hmc->hmc_id, new_hmc_id, HMC_ID_LEN); | ||
| 1234 | if (bytes) | ||
| 1235 | return -EFAULT; | ||
| 1236 | |||
| 1237 | /* Send Open Session command */ | ||
| 1238 | spin_lock_irqsave(&hmc->lock, flags); | ||
| 1239 | buffer = ibmvmc_get_valid_hmc_buffer(hmc->index); | ||
| 1240 | spin_unlock_irqrestore(&hmc->lock, flags); | ||
| 1241 | |||
| 1242 | if (!buffer || !buffer->real_addr_local) { | ||
| 1243 | pr_warn("ibmvmc: sethmcid: no buffer available\n"); | ||
| 1244 | return -EIO; | ||
| 1245 | } | ||
| 1246 | |||
| 1247 | /* Make sure buffer is NULL terminated before trying to print it */ | ||
| 1248 | memset(print_buffer, 0, HMC_ID_LEN + 1); | ||
| 1249 | strncpy(print_buffer, hmc->hmc_id, HMC_ID_LEN); | ||
| 1250 | pr_info("ibmvmc: sethmcid: Set HMC ID: \"%s\"\n", print_buffer); | ||
| 1251 | |||
| 1252 | memcpy(buffer->real_addr_local, hmc->hmc_id, HMC_ID_LEN); | ||
| 1253 | /* RDMA over ID, send open msg, change state to ibmhmc_state_opening */ | ||
| 1254 | rc = ibmvmc_send_open(buffer, hmc); | ||
| 1255 | |||
| 1256 | return rc; | ||
| 1257 | } | ||
| 1258 | |||
| 1259 | /** | ||
| 1260 | * ibmvmc_ioctl_query - IOCTL Query | ||
| 1261 | * | ||
| 1262 | * @session: ibmvmc_file_session struct | ||
| 1263 | * @ret_struct: ibmvmc_query_struct | ||
| 1264 | * | ||
| 1265 | * Return: | ||
| 1266 | * 0 - Success | ||
| 1267 | * Non-zero - Failure | ||
| 1268 | */ | ||
| 1269 | static long ibmvmc_ioctl_query(struct ibmvmc_file_session *session, | ||
| 1270 | struct ibmvmc_query_struct __user *ret_struct) | ||
| 1271 | { | ||
| 1272 | struct ibmvmc_query_struct query_struct; | ||
| 1273 | size_t bytes; | ||
| 1274 | |||
| 1275 | memset(&query_struct, 0, sizeof(query_struct)); | ||
| 1276 | query_struct.have_vmc = (ibmvmc.state > ibmvmc_state_initial); | ||
| 1277 | query_struct.state = ibmvmc.state; | ||
| 1278 | query_struct.vmc_drc_index = ibmvmc.vmc_drc_index; | ||
| 1279 | |||
| 1280 | bytes = copy_to_user(ret_struct, &query_struct, | ||
| 1281 | sizeof(query_struct)); | ||
| 1282 | if (bytes) | ||
| 1283 | return -EFAULT; | ||
| 1284 | |||
| 1285 | return 0; | ||
| 1286 | } | ||
| 1287 | |||
| 1288 | /** | ||
| 1289 | * ibmvmc_ioctl_requestvmc - IOCTL Request VMC | ||
| 1290 | * | ||
| 1291 | * @session: ibmvmc_file_session struct | ||
| 1292 | * @ret_vmc_index: VMC Index | ||
| 1293 | * | ||
| 1294 | * Return: | ||
| 1295 | * 0 - Success | ||
| 1296 | * Non-zero - Failure | ||
| 1297 | */ | ||
| 1298 | static long ibmvmc_ioctl_requestvmc(struct ibmvmc_file_session *session, | ||
| 1299 | u32 __user *ret_vmc_index) | ||
| 1300 | { | ||
| 1301 | /* TODO: (adreznec) Add locking to control multiple process access */ | ||
| 1302 | size_t bytes; | ||
| 1303 | long rc; | ||
| 1304 | u32 vmc_drc_index; | ||
| 1305 | |||
| 1306 | /* Call to request the VMC device from phyp*/ | ||
| 1307 | rc = h_request_vmc(&vmc_drc_index); | ||
| 1308 | pr_debug("ibmvmc: requestvmc: H_REQUEST_VMC rc = 0x%lx\n", rc); | ||
| 1309 | |||
| 1310 | if (rc == H_SUCCESS) { | ||
| 1311 | rc = 0; | ||
| 1312 | } else if (rc == H_FUNCTION) { | ||
| 1313 | pr_err("ibmvmc: requestvmc: h_request_vmc not supported\n"); | ||
| 1314 | return -EPERM; | ||
| 1315 | } else if (rc == H_AUTHORITY) { | ||
| 1316 | pr_err("ibmvmc: requestvmc: hypervisor denied vmc request\n"); | ||
| 1317 | return -EPERM; | ||
| 1318 | } else if (rc == H_HARDWARE) { | ||
| 1319 | pr_err("ibmvmc: requestvmc: hypervisor hardware fault\n"); | ||
| 1320 | return -EIO; | ||
| 1321 | } else if (rc == H_RESOURCE) { | ||
| 1322 | pr_err("ibmvmc: requestvmc: vmc resource unavailable\n"); | ||
| 1323 | return -ENODEV; | ||
| 1324 | } else if (rc == H_NOT_AVAILABLE) { | ||
| 1325 | pr_err("ibmvmc: requestvmc: system cannot be vmc managed\n"); | ||
| 1326 | return -EPERM; | ||
| 1327 | } else if (rc == H_PARAMETER) { | ||
| 1328 | pr_err("ibmvmc: requestvmc: invalid parameter\n"); | ||
| 1329 | return -EINVAL; | ||
| 1330 | } | ||
| 1331 | |||
| 1332 | /* Success, set the vmc index in global struct */ | ||
| 1333 | ibmvmc.vmc_drc_index = vmc_drc_index; | ||
| 1334 | |||
| 1335 | bytes = copy_to_user(ret_vmc_index, &vmc_drc_index, | ||
| 1336 | sizeof(*ret_vmc_index)); | ||
| 1337 | if (bytes) { | ||
| 1338 | pr_warn("ibmvmc: requestvmc: copy to user failed.\n"); | ||
| 1339 | return -EFAULT; | ||
| 1340 | } | ||
| 1341 | return rc; | ||
| 1342 | } | ||
| 1343 | |||
| 1344 | /** | ||
| 1345 | * ibmvmc_ioctl - IOCTL | ||
| 1346 | * | ||
| 1347 | * @session: ibmvmc_file_session struct | ||
| 1348 | * @cmd: cmd field | ||
| 1349 | * @arg: Argument field | ||
| 1350 | * | ||
| 1351 | * Return: | ||
| 1352 | * 0 - Success | ||
| 1353 | * Non-zero - Failure | ||
| 1354 | */ | ||
| 1355 | static long ibmvmc_ioctl(struct file *file, | ||
| 1356 | unsigned int cmd, unsigned long arg) | ||
| 1357 | { | ||
| 1358 | struct ibmvmc_file_session *session = file->private_data; | ||
| 1359 | |||
| 1360 | pr_debug("ibmvmc: ioctl file=0x%lx, cmd=0x%x, arg=0x%lx, ses=0x%lx\n", | ||
| 1361 | (unsigned long)file, cmd, arg, | ||
| 1362 | (unsigned long)session); | ||
| 1363 | |||
| 1364 | if (!session) { | ||
| 1365 | pr_warn("ibmvmc: ioctl: no session\n"); | ||
| 1366 | return -EIO; | ||
| 1367 | } | ||
| 1368 | |||
| 1369 | switch (cmd) { | ||
| 1370 | case VMC_IOCTL_SETHMCID: | ||
| 1371 | return ibmvmc_ioctl_sethmcid(session, | ||
| 1372 | (unsigned char __user *)arg); | ||
| 1373 | case VMC_IOCTL_QUERY: | ||
| 1374 | return ibmvmc_ioctl_query(session, | ||
| 1375 | (struct ibmvmc_query_struct __user *)arg); | ||
| 1376 | case VMC_IOCTL_REQUESTVMC: | ||
| 1377 | return ibmvmc_ioctl_requestvmc(session, | ||
| 1378 | (unsigned int __user *)arg); | ||
| 1379 | default: | ||
| 1380 | pr_warn("ibmvmc: unknown ioctl 0x%x\n", cmd); | ||
| 1381 | return -EINVAL; | ||
| 1382 | } | ||
| 1383 | } | ||
| 1384 | |||
| 1385 | static const struct file_operations ibmvmc_fops = { | ||
| 1386 | .owner = THIS_MODULE, | ||
| 1387 | .read = ibmvmc_read, | ||
| 1388 | .write = ibmvmc_write, | ||
| 1389 | .poll = ibmvmc_poll, | ||
| 1390 | .unlocked_ioctl = ibmvmc_ioctl, | ||
| 1391 | .open = ibmvmc_open, | ||
| 1392 | .release = ibmvmc_close, | ||
| 1393 | }; | ||
| 1394 | |||
| 1395 | /** | ||
| 1396 | * ibmvmc_add_buffer - Add Buffer | ||
| 1397 | * | ||
| 1398 | * @adapter: crq_server_adapter struct | ||
| 1399 | * @crq: ibmvmc_crq_msg struct | ||
| 1400 | * | ||
| 1401 | * This message transfers a buffer from hypervisor ownership to management | ||
| 1402 | * partition ownership. The LIOBA is obtained from the virtual TCE table | ||
| 1403 | * associated with the hypervisor side of the VMC device, and points to a | ||
| 1404 | * buffer of size MTU (as established in the capabilities exchange). | ||
| 1405 | * | ||
| 1406 | * Typical flow for ading buffers: | ||
| 1407 | * 1. A new management application connection is opened by the management | ||
| 1408 | * partition. | ||
| 1409 | * 2. The hypervisor assigns new buffers for the traffic associated with | ||
| 1410 | * that connection. | ||
| 1411 | * 3. The hypervisor sends VMC Add Buffer messages to the management | ||
| 1412 | * partition, informing it of the new buffers. | ||
| 1413 | * 4. The hypervisor sends an HMC protocol message (to the management | ||
| 1414 | * application) notifying it of the new buffers. This informs the | ||
| 1415 | * application that it has buffers available for sending HMC | ||
| 1416 | * commands. | ||
| 1417 | * | ||
| 1418 | * Return: | ||
| 1419 | * 0 - Success | ||
| 1420 | * Non-zero - Failure | ||
| 1421 | */ | ||
| 1422 | static int ibmvmc_add_buffer(struct crq_server_adapter *adapter, | ||
| 1423 | struct ibmvmc_crq_msg *crq) | ||
| 1424 | { | ||
| 1425 | struct ibmvmc_buffer *buffer; | ||
| 1426 | u8 hmc_index; | ||
| 1427 | u8 hmc_session; | ||
| 1428 | u16 buffer_id; | ||
| 1429 | unsigned long flags; | ||
| 1430 | int rc = 0; | ||
| 1431 | |||
| 1432 | if (!crq) | ||
| 1433 | return -1; | ||
| 1434 | |||
| 1435 | hmc_session = crq->hmc_session; | ||
| 1436 | hmc_index = crq->hmc_index; | ||
| 1437 | buffer_id = be16_to_cpu(crq->var2.buffer_id); | ||
| 1438 | |||
| 1439 | if (hmc_index > ibmvmc.max_hmc_index) { | ||
| 1440 | dev_err(adapter->dev, "add_buffer: invalid hmc_index = 0x%x\n", | ||
| 1441 | hmc_index); | ||
| 1442 | ibmvmc_send_add_buffer_resp(adapter, VMC_MSG_INVALID_HMC_INDEX, | ||
| 1443 | hmc_session, hmc_index, buffer_id); | ||
| 1444 | return -1; | ||
| 1445 | } | ||
| 1446 | |||
| 1447 | if (buffer_id >= ibmvmc.max_buffer_pool_size) { | ||
| 1448 | dev_err(adapter->dev, "add_buffer: invalid buffer_id = 0x%x\n", | ||
| 1449 | buffer_id); | ||
| 1450 | ibmvmc_send_add_buffer_resp(adapter, VMC_MSG_INVALID_BUFFER_ID, | ||
| 1451 | hmc_session, hmc_index, buffer_id); | ||
| 1452 | return -1; | ||
| 1453 | } | ||
| 1454 | |||
| 1455 | spin_lock_irqsave(&hmcs[hmc_index].lock, flags); | ||
| 1456 | buffer = &hmcs[hmc_index].buffer[buffer_id]; | ||
| 1457 | |||
| 1458 | if (buffer->real_addr_local || buffer->dma_addr_local) { | ||
| 1459 | dev_warn(adapter->dev, "add_buffer: already allocated id = 0x%lx\n", | ||
| 1460 | (unsigned long)buffer_id); | ||
| 1461 | spin_unlock_irqrestore(&hmcs[hmc_index].lock, flags); | ||
| 1462 | ibmvmc_send_add_buffer_resp(adapter, VMC_MSG_INVALID_BUFFER_ID, | ||
| 1463 | hmc_session, hmc_index, buffer_id); | ||
| 1464 | return -1; | ||
| 1465 | } | ||
| 1466 | |||
| 1467 | buffer->real_addr_local = alloc_dma_buffer(to_vio_dev(adapter->dev), | ||
| 1468 | ibmvmc.max_mtu, | ||
| 1469 | &buffer->dma_addr_local); | ||
| 1470 | |||
| 1471 | if (!buffer->real_addr_local) { | ||
| 1472 | dev_err(adapter->dev, "add_buffer: alloc_dma_buffer failed.\n"); | ||
| 1473 | spin_unlock_irqrestore(&hmcs[hmc_index].lock, flags); | ||
| 1474 | ibmvmc_send_add_buffer_resp(adapter, VMC_MSG_INTERFACE_FAILURE, | ||
| 1475 | hmc_session, hmc_index, buffer_id); | ||
| 1476 | return -1; | ||
| 1477 | } | ||
| 1478 | |||
| 1479 | buffer->dma_addr_remote = be32_to_cpu(crq->var3.lioba); | ||
| 1480 | buffer->size = ibmvmc.max_mtu; | ||
| 1481 | buffer->owner = crq->var1.owner; | ||
| 1482 | buffer->free = 1; | ||
| 1483 | /* Must ensure valid==1 is observable only after all other fields are */ | ||
| 1484 | dma_wmb(); | ||
| 1485 | buffer->valid = 1; | ||
| 1486 | buffer->id = buffer_id; | ||
| 1487 | |||
| 1488 | dev_dbg(adapter->dev, "add_buffer: successfully added a buffer:\n"); | ||
| 1489 | dev_dbg(adapter->dev, " index: %d, session: %d, buffer: 0x%x, owner: %d\n", | ||
| 1490 | hmc_index, hmc_session, buffer_id, buffer->owner); | ||
| 1491 | dev_dbg(adapter->dev, " local: 0x%x, remote: 0x%x\n", | ||
| 1492 | (u32)buffer->dma_addr_local, | ||
| 1493 | (u32)buffer->dma_addr_remote); | ||
| 1494 | spin_unlock_irqrestore(&hmcs[hmc_index].lock, flags); | ||
| 1495 | |||
| 1496 | ibmvmc_send_add_buffer_resp(adapter, VMC_MSG_SUCCESS, hmc_session, | ||
| 1497 | hmc_index, buffer_id); | ||
| 1498 | |||
| 1499 | return rc; | ||
| 1500 | } | ||
| 1501 | |||
| 1502 | /** | ||
| 1503 | * ibmvmc_rem_buffer - Remove Buffer | ||
| 1504 | * | ||
| 1505 | * @adapter: crq_server_adapter struct | ||
| 1506 | * @crq: ibmvmc_crq_msg struct | ||
| 1507 | * | ||
| 1508 | * This message requests an HMC buffer to be transferred from management | ||
| 1509 | * partition ownership to hypervisor ownership. The management partition may | ||
| 1510 | * not be able to satisfy the request at a particular point in time if all its | ||
| 1511 | * buffers are in use. The management partition requires a depth of at least | ||
| 1512 | * one inbound buffer to allow management application commands to flow to the | ||
| 1513 | * hypervisor. It is, therefore, an interface error for the hypervisor to | ||
| 1514 | * attempt to remove the management partition's last buffer. | ||
| 1515 | * | ||
| 1516 | * The hypervisor is expected to manage buffer usage with the management | ||
| 1517 | * application directly and inform the management partition when buffers may be | ||
| 1518 | * removed. The typical flow for removing buffers: | ||
| 1519 | * | ||
| 1520 | * 1. The management application no longer needs a communication path to a | ||
| 1521 | * particular hypervisor function. That function is closed. | ||
| 1522 | * 2. The hypervisor and the management application quiesce all traffic to that | ||
| 1523 | * function. The hypervisor requests a reduction in buffer pool size. | ||
| 1524 | * 3. The management application acknowledges the reduction in buffer pool size. | ||
| 1525 | * 4. The hypervisor sends a Remove Buffer message to the management partition, | ||
| 1526 | * informing it of the reduction in buffers. | ||
| 1527 | * 5. The management partition verifies it can remove the buffer. This is | ||
| 1528 | * possible if buffers have been quiesced. | ||
| 1529 | * | ||
| 1530 | * Return: | ||
| 1531 | * 0 - Success | ||
| 1532 | * Non-zero - Failure | ||
| 1533 | */ | ||
| 1534 | /* | ||
| 1535 | * The hypervisor requested that we pick an unused buffer, and return it. | ||
| 1536 | * Before sending the buffer back, we free any storage associated with the | ||
| 1537 | * buffer. | ||
| 1538 | */ | ||
| 1539 | static int ibmvmc_rem_buffer(struct crq_server_adapter *adapter, | ||
| 1540 | struct ibmvmc_crq_msg *crq) | ||
| 1541 | { | ||
| 1542 | struct ibmvmc_buffer *buffer; | ||
| 1543 | u8 hmc_index; | ||
| 1544 | u8 hmc_session; | ||
| 1545 | u16 buffer_id = 0; | ||
| 1546 | unsigned long flags; | ||
| 1547 | int rc = 0; | ||
| 1548 | |||
| 1549 | if (!crq) | ||
| 1550 | return -1; | ||
| 1551 | |||
| 1552 | hmc_session = crq->hmc_session; | ||
| 1553 | hmc_index = crq->hmc_index; | ||
| 1554 | |||
| 1555 | if (hmc_index > ibmvmc.max_hmc_index) { | ||
| 1556 | dev_warn(adapter->dev, "rem_buffer: invalid hmc_index = 0x%x\n", | ||
| 1557 | hmc_index); | ||
| 1558 | ibmvmc_send_rem_buffer_resp(adapter, VMC_MSG_INVALID_HMC_INDEX, | ||
| 1559 | hmc_session, hmc_index, buffer_id); | ||
| 1560 | return -1; | ||
| 1561 | } | ||
| 1562 | |||
| 1563 | spin_lock_irqsave(&hmcs[hmc_index].lock, flags); | ||
| 1564 | buffer = ibmvmc_get_free_hmc_buffer(adapter, hmc_index); | ||
| 1565 | if (!buffer) { | ||
| 1566 | dev_info(adapter->dev, "rem_buffer: no buffer to remove\n"); | ||
| 1567 | spin_unlock_irqrestore(&hmcs[hmc_index].lock, flags); | ||
| 1568 | ibmvmc_send_rem_buffer_resp(adapter, VMC_MSG_NO_BUFFER, | ||
| 1569 | hmc_session, hmc_index, | ||
| 1570 | VMC_INVALID_BUFFER_ID); | ||
| 1571 | return -1; | ||
| 1572 | } | ||
| 1573 | |||
| 1574 | buffer_id = buffer->id; | ||
| 1575 | |||
| 1576 | if (buffer->valid) | ||
| 1577 | free_dma_buffer(to_vio_dev(adapter->dev), | ||
| 1578 | ibmvmc.max_mtu, | ||
| 1579 | buffer->real_addr_local, | ||
| 1580 | buffer->dma_addr_local); | ||
| 1581 | |||
| 1582 | memset(buffer, 0, sizeof(struct ibmvmc_buffer)); | ||
| 1583 | spin_unlock_irqrestore(&hmcs[hmc_index].lock, flags); | ||
| 1584 | |||
| 1585 | dev_dbg(adapter->dev, "rem_buffer: removed buffer 0x%x.\n", buffer_id); | ||
| 1586 | ibmvmc_send_rem_buffer_resp(adapter, VMC_MSG_SUCCESS, hmc_session, | ||
| 1587 | hmc_index, buffer_id); | ||
| 1588 | |||
| 1589 | return rc; | ||
| 1590 | } | ||
| 1591 | |||
| 1592 | static int ibmvmc_recv_msg(struct crq_server_adapter *adapter, | ||
| 1593 | struct ibmvmc_crq_msg *crq) | ||
| 1594 | { | ||
| 1595 | struct ibmvmc_buffer *buffer; | ||
| 1596 | struct ibmvmc_hmc *hmc; | ||
| 1597 | unsigned long msg_len; | ||
| 1598 | u8 hmc_index; | ||
| 1599 | u8 hmc_session; | ||
| 1600 | u16 buffer_id; | ||
| 1601 | unsigned long flags; | ||
| 1602 | int rc = 0; | ||
| 1603 | |||
| 1604 | if (!crq) | ||
| 1605 | return -1; | ||
| 1606 | |||
| 1607 | /* Hypervisor writes CRQs directly into our memory in big endian */ | ||
| 1608 | dev_dbg(adapter->dev, "Recv_msg: msg from HV 0x%016llx 0x%016llx\n", | ||
| 1609 | be64_to_cpu(*((unsigned long *)crq)), | ||
| 1610 | be64_to_cpu(*(((unsigned long *)crq) + 1))); | ||
| 1611 | |||
| 1612 | hmc_session = crq->hmc_session; | ||
| 1613 | hmc_index = crq->hmc_index; | ||
| 1614 | buffer_id = be16_to_cpu(crq->var2.buffer_id); | ||
| 1615 | msg_len = be32_to_cpu(crq->var3.msg_len); | ||
| 1616 | |||
| 1617 | if (hmc_index > ibmvmc.max_hmc_index) { | ||
| 1618 | dev_err(adapter->dev, "Recv_msg: invalid hmc_index = 0x%x\n", | ||
| 1619 | hmc_index); | ||
| 1620 | ibmvmc_send_add_buffer_resp(adapter, VMC_MSG_INVALID_HMC_INDEX, | ||
| 1621 | hmc_session, hmc_index, buffer_id); | ||
| 1622 | return -1; | ||
| 1623 | } | ||
| 1624 | |||
| 1625 | if (buffer_id >= ibmvmc.max_buffer_pool_size) { | ||
| 1626 | dev_err(adapter->dev, "Recv_msg: invalid buffer_id = 0x%x\n", | ||
| 1627 | buffer_id); | ||
| 1628 | ibmvmc_send_add_buffer_resp(adapter, VMC_MSG_INVALID_BUFFER_ID, | ||
| 1629 | hmc_session, hmc_index, buffer_id); | ||
| 1630 | return -1; | ||
| 1631 | } | ||
| 1632 | |||
| 1633 | hmc = &hmcs[hmc_index]; | ||
| 1634 | spin_lock_irqsave(&hmc->lock, flags); | ||
| 1635 | |||
| 1636 | if (hmc->state == ibmhmc_state_free) { | ||
| 1637 | dev_err(adapter->dev, "Recv_msg: invalid hmc state = 0x%x\n", | ||
| 1638 | hmc->state); | ||
| 1639 | /* HMC connection is not valid (possibly was reset under us). */ | ||
| 1640 | spin_unlock_irqrestore(&hmc->lock, flags); | ||
| 1641 | return -1; | ||
| 1642 | } | ||
| 1643 | |||
| 1644 | buffer = &hmc->buffer[buffer_id]; | ||
| 1645 | |||
| 1646 | if (buffer->valid == 0 || buffer->owner == VMC_BUF_OWNER_ALPHA) { | ||
| 1647 | dev_err(adapter->dev, "Recv_msg: not valid, or not HV. 0x%x 0x%x\n", | ||
| 1648 | buffer->valid, buffer->owner); | ||
| 1649 | spin_unlock_irqrestore(&hmc->lock, flags); | ||
| 1650 | return -1; | ||
| 1651 | } | ||
| 1652 | |||
| 1653 | /* RDMA the data into the partition. */ | ||
| 1654 | rc = h_copy_rdma(msg_len, | ||
| 1655 | adapter->riobn, | ||
| 1656 | buffer->dma_addr_remote, | ||
| 1657 | adapter->liobn, | ||
| 1658 | buffer->dma_addr_local); | ||
| 1659 | |||
| 1660 | dev_dbg(adapter->dev, "Recv_msg: msg_len = 0x%x, buffer_id = 0x%x, queue_head = 0x%x, hmc_idx = 0x%x\n", | ||
| 1661 | (unsigned int)msg_len, (unsigned int)buffer_id, | ||
| 1662 | (unsigned int)hmc->queue_head, (unsigned int)hmc_index); | ||
| 1663 | buffer->msg_len = msg_len; | ||
| 1664 | buffer->free = 0; | ||
| 1665 | buffer->owner = VMC_BUF_OWNER_ALPHA; | ||
| 1666 | |||
| 1667 | if (rc) { | ||
| 1668 | dev_err(adapter->dev, "Failure in recv_msg: h_copy_rdma = 0x%x\n", | ||
| 1669 | rc); | ||
| 1670 | spin_unlock_irqrestore(&hmc->lock, flags); | ||
| 1671 | return -1; | ||
| 1672 | } | ||
| 1673 | |||
| 1674 | /* Must be locked because read operates on the same data */ | ||
| 1675 | hmc->queue_outbound_msgs[hmc->queue_head] = buffer_id; | ||
| 1676 | hmc->queue_head++; | ||
| 1677 | if (hmc->queue_head == ibmvmc_max_buf_pool_size) | ||
| 1678 | hmc->queue_head = 0; | ||
| 1679 | |||
| 1680 | if (hmc->queue_head == hmc->queue_tail) | ||
| 1681 | dev_err(adapter->dev, "outbound buffer queue wrapped.\n"); | ||
| 1682 | |||
| 1683 | spin_unlock_irqrestore(&hmc->lock, flags); | ||
| 1684 | |||
| 1685 | wake_up_interruptible(&ibmvmc_read_wait); | ||
| 1686 | |||
| 1687 | return 0; | ||
| 1688 | } | ||
| 1689 | |||
| 1690 | /** | ||
| 1691 | * ibmvmc_process_capabilities - Process Capabilities | ||
| 1692 | * | ||
| 1693 | * @adapter: crq_server_adapter struct | ||
| 1694 | * @crqp: ibmvmc_crq_msg struct | ||
| 1695 | * | ||
| 1696 | */ | ||
| 1697 | static void ibmvmc_process_capabilities(struct crq_server_adapter *adapter, | ||
| 1698 | struct ibmvmc_crq_msg *crqp) | ||
| 1699 | { | ||
| 1700 | struct ibmvmc_admin_crq_msg *crq = (struct ibmvmc_admin_crq_msg *)crqp; | ||
| 1701 | |||
| 1702 | if ((be16_to_cpu(crq->version) >> 8) != | ||
| 1703 | (IBMVMC_PROTOCOL_VERSION >> 8)) { | ||
| 1704 | dev_err(adapter->dev, "init failed, incompatible versions 0x%x 0x%x\n", | ||
| 1705 | be16_to_cpu(crq->version), | ||
| 1706 | IBMVMC_PROTOCOL_VERSION); | ||
| 1707 | ibmvmc.state = ibmvmc_state_failed; | ||
| 1708 | return; | ||
| 1709 | } | ||
| 1710 | |||
| 1711 | ibmvmc.max_mtu = min_t(u32, ibmvmc_max_mtu, be32_to_cpu(crq->max_mtu)); | ||
| 1712 | ibmvmc.max_buffer_pool_size = min_t(u16, ibmvmc_max_buf_pool_size, | ||
| 1713 | be16_to_cpu(crq->pool_size)); | ||
| 1714 | ibmvmc.max_hmc_index = min_t(u8, ibmvmc_max_hmcs, crq->max_hmc) - 1; | ||
| 1715 | ibmvmc.state = ibmvmc_state_ready; | ||
| 1716 | |||
| 1717 | dev_info(adapter->dev, "Capabilities: mtu=0x%x, pool_size=0x%x, max_hmc=0x%x\n", | ||
| 1718 | ibmvmc.max_mtu, ibmvmc.max_buffer_pool_size, | ||
| 1719 | ibmvmc.max_hmc_index); | ||
| 1720 | } | ||
| 1721 | |||
| 1722 | /** | ||
| 1723 | * ibmvmc_validate_hmc_session - Validate HMC Session | ||
| 1724 | * | ||
| 1725 | * @adapter: crq_server_adapter struct | ||
| 1726 | * @crq: ibmvmc_crq_msg struct | ||
| 1727 | * | ||
| 1728 | * Return: | ||
| 1729 | * 0 - Success | ||
| 1730 | * Non-zero - Failure | ||
| 1731 | */ | ||
| 1732 | static int ibmvmc_validate_hmc_session(struct crq_server_adapter *adapter, | ||
| 1733 | struct ibmvmc_crq_msg *crq) | ||
| 1734 | { | ||
| 1735 | unsigned char hmc_index; | ||
| 1736 | |||
| 1737 | hmc_index = crq->hmc_index; | ||
| 1738 | |||
| 1739 | if (crq->hmc_session == 0) | ||
| 1740 | return 0; | ||
| 1741 | |||
| 1742 | if (hmc_index > ibmvmc.max_hmc_index) | ||
| 1743 | return -1; | ||
| 1744 | |||
| 1745 | if (hmcs[hmc_index].session != crq->hmc_session) { | ||
| 1746 | dev_warn(adapter->dev, "Drop, bad session: expected 0x%x, recv 0x%x\n", | ||
| 1747 | hmcs[hmc_index].session, crq->hmc_session); | ||
| 1748 | return -1; | ||
| 1749 | } | ||
| 1750 | |||
| 1751 | return 0; | ||
| 1752 | } | ||
| 1753 | |||
| 1754 | /** | ||
| 1755 | * ibmvmc_reset - Reset | ||
| 1756 | * | ||
| 1757 | * @adapter: crq_server_adapter struct | ||
| 1758 | * @xport_event: export_event field | ||
| 1759 | * | ||
| 1760 | * Closes all HMC sessions and conditionally schedules a CRQ reset. | ||
| 1761 | * @xport_event: If true, the partner closed their CRQ; we don't need to reset. | ||
| 1762 | * If false, we need to schedule a CRQ reset. | ||
| 1763 | */ | ||
| 1764 | static void ibmvmc_reset(struct crq_server_adapter *adapter, bool xport_event) | ||
| 1765 | { | ||
| 1766 | int i; | ||
| 1767 | |||
| 1768 | if (ibmvmc.state != ibmvmc_state_sched_reset) { | ||
| 1769 | dev_info(adapter->dev, "*** Reset to initial state.\n"); | ||
| 1770 | for (i = 0; i < ibmvmc_max_hmcs; i++) | ||
| 1771 | ibmvmc_return_hmc(&hmcs[i], xport_event); | ||
| 1772 | |||
| 1773 | if (xport_event) { | ||
| 1774 | /* CRQ was closed by the partner. We don't need to do | ||
| 1775 | * anything except set ourself to the correct state to | ||
| 1776 | * handle init msgs. | ||
| 1777 | */ | ||
| 1778 | ibmvmc.state = ibmvmc_state_crqinit; | ||
| 1779 | } else { | ||
| 1780 | /* The partner did not close their CRQ - instead, we're | ||
| 1781 | * closing the CRQ on our end. Need to schedule this | ||
| 1782 | * for process context, because CRQ reset may require a | ||
| 1783 | * sleep. | ||
| 1784 | * | ||
| 1785 | * Setting ibmvmc.state here immediately prevents | ||
| 1786 | * ibmvmc_open from completing until the reset | ||
| 1787 | * completes in process context. | ||
| 1788 | */ | ||
| 1789 | ibmvmc.state = ibmvmc_state_sched_reset; | ||
| 1790 | dev_dbg(adapter->dev, "Device reset scheduled"); | ||
| 1791 | wake_up_interruptible(&adapter->reset_wait_queue); | ||
| 1792 | } | ||
| 1793 | } | ||
| 1794 | } | ||
| 1795 | |||
| 1796 | /** | ||
| 1797 | * ibmvmc_reset_task - Reset Task | ||
| 1798 | * | ||
| 1799 | * @data: Data field | ||
| 1800 | * | ||
| 1801 | * Performs a CRQ reset of the VMC device in process context. | ||
| 1802 | * NOTE: This function should not be called directly, use ibmvmc_reset. | ||
| 1803 | */ | ||
| 1804 | static int ibmvmc_reset_task(void *data) | ||
| 1805 | { | ||
| 1806 | struct crq_server_adapter *adapter = data; | ||
| 1807 | int rc; | ||
| 1808 | |||
| 1809 | set_user_nice(current, -20); | ||
| 1810 | |||
| 1811 | while (!kthread_should_stop()) { | ||
| 1812 | wait_event_interruptible(adapter->reset_wait_queue, | ||
| 1813 | (ibmvmc.state == ibmvmc_state_sched_reset) || | ||
| 1814 | kthread_should_stop()); | ||
| 1815 | |||
| 1816 | if (kthread_should_stop()) | ||
| 1817 | break; | ||
| 1818 | |||
| 1819 | dev_dbg(adapter->dev, "CRQ resetting in process context"); | ||
| 1820 | tasklet_disable(&adapter->work_task); | ||
| 1821 | |||
| 1822 | rc = ibmvmc_reset_crq_queue(adapter); | ||
| 1823 | |||
| 1824 | if (rc != H_SUCCESS && rc != H_RESOURCE) { | ||
| 1825 | dev_err(adapter->dev, "Error initializing CRQ. rc = 0x%x\n", | ||
| 1826 | rc); | ||
| 1827 | ibmvmc.state = ibmvmc_state_failed; | ||
| 1828 | } else { | ||
| 1829 | ibmvmc.state = ibmvmc_state_crqinit; | ||
| 1830 | |||
| 1831 | if (ibmvmc_send_crq(adapter, 0xC001000000000000LL, 0) | ||
| 1832 | != 0 && rc != H_RESOURCE) | ||
| 1833 | dev_warn(adapter->dev, "Failed to send initialize CRQ message\n"); | ||
| 1834 | } | ||
| 1835 | |||
| 1836 | vio_enable_interrupts(to_vio_dev(adapter->dev)); | ||
| 1837 | tasklet_enable(&adapter->work_task); | ||
| 1838 | } | ||
| 1839 | |||
| 1840 | return 0; | ||
| 1841 | } | ||
| 1842 | |||
| 1843 | /** | ||
| 1844 | * ibmvmc_process_open_resp - Process Open Response | ||
| 1845 | * | ||
| 1846 | * @crq: ibmvmc_crq_msg struct | ||
| 1847 | * @adapter: crq_server_adapter struct | ||
| 1848 | * | ||
| 1849 | * This command is sent by the hypervisor in response to the Interface | ||
| 1850 | * Open message. When this message is received, the indicated buffer is | ||
| 1851 | * again available for management partition use. | ||
| 1852 | */ | ||
| 1853 | static void ibmvmc_process_open_resp(struct ibmvmc_crq_msg *crq, | ||
| 1854 | struct crq_server_adapter *adapter) | ||
| 1855 | { | ||
| 1856 | unsigned char hmc_index; | ||
| 1857 | unsigned short buffer_id; | ||
| 1858 | |||
| 1859 | hmc_index = crq->hmc_index; | ||
| 1860 | if (hmc_index > ibmvmc.max_hmc_index) { | ||
| 1861 | /* Why would PHYP give an index > max negotiated? */ | ||
| 1862 | ibmvmc_reset(adapter, false); | ||
| 1863 | return; | ||
| 1864 | } | ||
| 1865 | |||
| 1866 | if (crq->status) { | ||
| 1867 | dev_warn(adapter->dev, "open_resp: failed - status 0x%x\n", | ||
| 1868 | crq->status); | ||
| 1869 | ibmvmc_return_hmc(&hmcs[hmc_index], false); | ||
| 1870 | return; | ||
| 1871 | } | ||
| 1872 | |||
| 1873 | if (hmcs[hmc_index].state == ibmhmc_state_opening) { | ||
| 1874 | buffer_id = be16_to_cpu(crq->var2.buffer_id); | ||
| 1875 | if (buffer_id >= ibmvmc.max_buffer_pool_size) { | ||
| 1876 | dev_err(adapter->dev, "open_resp: invalid buffer_id = 0x%x\n", | ||
| 1877 | buffer_id); | ||
| 1878 | hmcs[hmc_index].state = ibmhmc_state_failed; | ||
| 1879 | } else { | ||
| 1880 | ibmvmc_free_hmc_buffer(&hmcs[hmc_index], | ||
| 1881 | &hmcs[hmc_index].buffer[buffer_id]); | ||
| 1882 | hmcs[hmc_index].state = ibmhmc_state_ready; | ||
| 1883 | dev_dbg(adapter->dev, "open_resp: set hmc state = ready\n"); | ||
| 1884 | } | ||
| 1885 | } else { | ||
| 1886 | dev_warn(adapter->dev, "open_resp: invalid hmc state (0x%x)\n", | ||
| 1887 | hmcs[hmc_index].state); | ||
| 1888 | } | ||
| 1889 | } | ||
| 1890 | |||
| 1891 | /** | ||
| 1892 | * ibmvmc_process_close_resp - Process Close Response | ||
| 1893 | * | ||
| 1894 | * @crq: ibmvmc_crq_msg struct | ||
| 1895 | * @adapter: crq_server_adapter struct | ||
| 1896 | * | ||
| 1897 | * This command is sent by the hypervisor in response to the managemant | ||
| 1898 | * application Interface Close message. | ||
| 1899 | * | ||
| 1900 | * If the close fails, simply reset the entire driver as the state of the VMC | ||
| 1901 | * must be in tough shape. | ||
| 1902 | */ | ||
| 1903 | static void ibmvmc_process_close_resp(struct ibmvmc_crq_msg *crq, | ||
| 1904 | struct crq_server_adapter *adapter) | ||
| 1905 | { | ||
| 1906 | unsigned char hmc_index; | ||
| 1907 | |||
| 1908 | hmc_index = crq->hmc_index; | ||
| 1909 | if (hmc_index > ibmvmc.max_hmc_index) { | ||
| 1910 | ibmvmc_reset(adapter, false); | ||
| 1911 | return; | ||
| 1912 | } | ||
| 1913 | |||
| 1914 | if (crq->status) { | ||
| 1915 | dev_warn(adapter->dev, "close_resp: failed - status 0x%x\n", | ||
| 1916 | crq->status); | ||
| 1917 | ibmvmc_reset(adapter, false); | ||
| 1918 | return; | ||
| 1919 | } | ||
| 1920 | |||
| 1921 | ibmvmc_return_hmc(&hmcs[hmc_index], false); | ||
| 1922 | } | ||
| 1923 | |||
| 1924 | /** | ||
| 1925 | * ibmvmc_crq_process - Process CRQ | ||
| 1926 | * | ||
| 1927 | * @adapter: crq_server_adapter struct | ||
| 1928 | * @crq: ibmvmc_crq_msg struct | ||
| 1929 | * | ||
| 1930 | * Process the CRQ message based upon the type of message received. | ||
| 1931 | * | ||
| 1932 | */ | ||
| 1933 | static void ibmvmc_crq_process(struct crq_server_adapter *adapter, | ||
| 1934 | struct ibmvmc_crq_msg *crq) | ||
| 1935 | { | ||
| 1936 | switch (crq->type) { | ||
| 1937 | case VMC_MSG_CAP_RESP: | ||
| 1938 | dev_dbg(adapter->dev, "CRQ recv: capabilities resp (0x%x)\n", | ||
| 1939 | crq->type); | ||
| 1940 | if (ibmvmc.state == ibmvmc_state_capabilities) | ||
| 1941 | ibmvmc_process_capabilities(adapter, crq); | ||
| 1942 | else | ||
| 1943 | dev_warn(adapter->dev, "caps msg invalid in state 0x%x\n", | ||
| 1944 | ibmvmc.state); | ||
| 1945 | break; | ||
| 1946 | case VMC_MSG_OPEN_RESP: | ||
| 1947 | dev_dbg(adapter->dev, "CRQ recv: open resp (0x%x)\n", | ||
| 1948 | crq->type); | ||
| 1949 | if (ibmvmc_validate_hmc_session(adapter, crq) == 0) | ||
| 1950 | ibmvmc_process_open_resp(crq, adapter); | ||
| 1951 | break; | ||
| 1952 | case VMC_MSG_ADD_BUF: | ||
| 1953 | dev_dbg(adapter->dev, "CRQ recv: add buf (0x%x)\n", | ||
| 1954 | crq->type); | ||
| 1955 | if (ibmvmc_validate_hmc_session(adapter, crq) == 0) | ||
| 1956 | ibmvmc_add_buffer(adapter, crq); | ||
| 1957 | break; | ||
| 1958 | case VMC_MSG_REM_BUF: | ||
| 1959 | dev_dbg(adapter->dev, "CRQ recv: rem buf (0x%x)\n", | ||
| 1960 | crq->type); | ||
| 1961 | if (ibmvmc_validate_hmc_session(adapter, crq) == 0) | ||
| 1962 | ibmvmc_rem_buffer(adapter, crq); | ||
| 1963 | break; | ||
| 1964 | case VMC_MSG_SIGNAL: | ||
| 1965 | dev_dbg(adapter->dev, "CRQ recv: signal msg (0x%x)\n", | ||
| 1966 | crq->type); | ||
| 1967 | if (ibmvmc_validate_hmc_session(adapter, crq) == 0) | ||
| 1968 | ibmvmc_recv_msg(adapter, crq); | ||
| 1969 | break; | ||
| 1970 | case VMC_MSG_CLOSE_RESP: | ||
| 1971 | dev_dbg(adapter->dev, "CRQ recv: close resp (0x%x)\n", | ||
| 1972 | crq->type); | ||
| 1973 | if (ibmvmc_validate_hmc_session(adapter, crq) == 0) | ||
| 1974 | ibmvmc_process_close_resp(crq, adapter); | ||
| 1975 | break; | ||
| 1976 | case VMC_MSG_CAP: | ||
| 1977 | case VMC_MSG_OPEN: | ||
| 1978 | case VMC_MSG_CLOSE: | ||
| 1979 | case VMC_MSG_ADD_BUF_RESP: | ||
| 1980 | case VMC_MSG_REM_BUF_RESP: | ||
| 1981 | dev_warn(adapter->dev, "CRQ recv: unexpected msg (0x%x)\n", | ||
| 1982 | crq->type); | ||
| 1983 | break; | ||
| 1984 | default: | ||
| 1985 | dev_warn(adapter->dev, "CRQ recv: unknown msg (0x%x)\n", | ||
| 1986 | crq->type); | ||
| 1987 | break; | ||
| 1988 | } | ||
| 1989 | } | ||
| 1990 | |||
| 1991 | /** | ||
| 1992 | * ibmvmc_handle_crq_init - Handle CRQ Init | ||
| 1993 | * | ||
| 1994 | * @crq: ibmvmc_crq_msg struct | ||
| 1995 | * @adapter: crq_server_adapter struct | ||
| 1996 | * | ||
| 1997 | * Handle the type of crq initialization based on whether | ||
| 1998 | * it is a message or a response. | ||
| 1999 | * | ||
| 2000 | */ | ||
| 2001 | static void ibmvmc_handle_crq_init(struct ibmvmc_crq_msg *crq, | ||
| 2002 | struct crq_server_adapter *adapter) | ||
| 2003 | { | ||
| 2004 | switch (crq->type) { | ||
| 2005 | case 0x01: /* Initialization message */ | ||
| 2006 | dev_dbg(adapter->dev, "CRQ recv: CRQ init msg - state 0x%x\n", | ||
| 2007 | ibmvmc.state); | ||
| 2008 | if (ibmvmc.state == ibmvmc_state_crqinit) { | ||
| 2009 | /* Send back a response */ | ||
| 2010 | if (ibmvmc_send_crq(adapter, 0xC002000000000000, | ||
| 2011 | 0) == 0) | ||
| 2012 | ibmvmc_send_capabilities(adapter); | ||
| 2013 | else | ||
| 2014 | dev_err(adapter->dev, " Unable to send init rsp\n"); | ||
| 2015 | } else { | ||
| 2016 | dev_err(adapter->dev, "Invalid state 0x%x mtu = 0x%x\n", | ||
| 2017 | ibmvmc.state, ibmvmc.max_mtu); | ||
| 2018 | } | ||
| 2019 | |||
| 2020 | break; | ||
| 2021 | case 0x02: /* Initialization response */ | ||
| 2022 | dev_dbg(adapter->dev, "CRQ recv: initialization resp msg - state 0x%x\n", | ||
| 2023 | ibmvmc.state); | ||
| 2024 | if (ibmvmc.state == ibmvmc_state_crqinit) | ||
| 2025 | ibmvmc_send_capabilities(adapter); | ||
| 2026 | break; | ||
| 2027 | default: | ||
| 2028 | dev_warn(adapter->dev, "Unknown crq message type 0x%lx\n", | ||
| 2029 | (unsigned long)crq->type); | ||
| 2030 | } | ||
| 2031 | } | ||
| 2032 | |||
| 2033 | /** | ||
| 2034 | * ibmvmc_handle_crq - Handle CRQ | ||
| 2035 | * | ||
| 2036 | * @crq: ibmvmc_crq_msg struct | ||
| 2037 | * @adapter: crq_server_adapter struct | ||
| 2038 | * | ||
| 2039 | * Read the command elements from the command queue and execute the | ||
| 2040 | * requests based upon the type of crq message. | ||
| 2041 | * | ||
| 2042 | */ | ||
| 2043 | static void ibmvmc_handle_crq(struct ibmvmc_crq_msg *crq, | ||
| 2044 | struct crq_server_adapter *adapter) | ||
| 2045 | { | ||
| 2046 | switch (crq->valid) { | ||
| 2047 | case 0xC0: /* initialization */ | ||
| 2048 | ibmvmc_handle_crq_init(crq, adapter); | ||
| 2049 | break; | ||
| 2050 | case 0xFF: /* Hypervisor telling us the connection is closed */ | ||
| 2051 | dev_warn(adapter->dev, "CRQ recv: virtual adapter failed - resetting.\n"); | ||
| 2052 | ibmvmc_reset(adapter, true); | ||
| 2053 | break; | ||
| 2054 | case 0x80: /* real payload */ | ||
| 2055 | ibmvmc_crq_process(adapter, crq); | ||
| 2056 | break; | ||
| 2057 | default: | ||
| 2058 | dev_warn(adapter->dev, "CRQ recv: unknown msg 0x%02x.\n", | ||
| 2059 | crq->valid); | ||
| 2060 | break; | ||
| 2061 | } | ||
| 2062 | } | ||
| 2063 | |||
| 2064 | static void ibmvmc_task(unsigned long data) | ||
| 2065 | { | ||
| 2066 | struct crq_server_adapter *adapter = | ||
| 2067 | (struct crq_server_adapter *)data; | ||
| 2068 | struct vio_dev *vdev = to_vio_dev(adapter->dev); | ||
| 2069 | struct ibmvmc_crq_msg *crq; | ||
| 2070 | int done = 0; | ||
| 2071 | |||
| 2072 | while (!done) { | ||
| 2073 | /* Pull all the valid messages off the CRQ */ | ||
| 2074 | while ((crq = crq_queue_next_crq(&adapter->queue)) != NULL) { | ||
| 2075 | ibmvmc_handle_crq(crq, adapter); | ||
| 2076 | crq->valid = 0x00; | ||
| 2077 | /* CRQ reset was requested, stop processing CRQs. | ||
| 2078 | * Interrupts will be re-enabled by the reset task. | ||
| 2079 | */ | ||
| 2080 | if (ibmvmc.state == ibmvmc_state_sched_reset) | ||
| 2081 | return; | ||
| 2082 | } | ||
| 2083 | |||
| 2084 | vio_enable_interrupts(vdev); | ||
| 2085 | crq = crq_queue_next_crq(&adapter->queue); | ||
| 2086 | if (crq) { | ||
| 2087 | vio_disable_interrupts(vdev); | ||
| 2088 | ibmvmc_handle_crq(crq, adapter); | ||
| 2089 | crq->valid = 0x00; | ||
| 2090 | /* CRQ reset was requested, stop processing CRQs. | ||
| 2091 | * Interrupts will be re-enabled by the reset task. | ||
| 2092 | */ | ||
| 2093 | if (ibmvmc.state == ibmvmc_state_sched_reset) | ||
| 2094 | return; | ||
| 2095 | } else { | ||
| 2096 | done = 1; | ||
| 2097 | } | ||
| 2098 | } | ||
| 2099 | } | ||
| 2100 | |||
| 2101 | /** | ||
| 2102 | * ibmvmc_init_crq_queue - Init CRQ Queue | ||
| 2103 | * | ||
| 2104 | * @adapter: crq_server_adapter struct | ||
| 2105 | * | ||
| 2106 | * Return: | ||
| 2107 | * 0 - Success | ||
| 2108 | * Non-zero - Failure | ||
| 2109 | */ | ||
| 2110 | static int ibmvmc_init_crq_queue(struct crq_server_adapter *adapter) | ||
| 2111 | { | ||
| 2112 | struct vio_dev *vdev = to_vio_dev(adapter->dev); | ||
| 2113 | struct crq_queue *queue = &adapter->queue; | ||
| 2114 | int rc = 0; | ||
| 2115 | int retrc = 0; | ||
| 2116 | |||
| 2117 | queue->msgs = (struct ibmvmc_crq_msg *)get_zeroed_page(GFP_KERNEL); | ||
| 2118 | |||
| 2119 | if (!queue->msgs) | ||
| 2120 | goto malloc_failed; | ||
| 2121 | |||
| 2122 | queue->size = PAGE_SIZE / sizeof(*queue->msgs); | ||
| 2123 | |||
| 2124 | queue->msg_token = dma_map_single(adapter->dev, queue->msgs, | ||
| 2125 | queue->size * sizeof(*queue->msgs), | ||
| 2126 | DMA_BIDIRECTIONAL); | ||
| 2127 | |||
| 2128 | if (dma_mapping_error(adapter->dev, queue->msg_token)) | ||
| 2129 | goto map_failed; | ||
| 2130 | |||
| 2131 | retrc = plpar_hcall_norets(H_REG_CRQ, | ||
| 2132 | vdev->unit_address, | ||
| 2133 | queue->msg_token, PAGE_SIZE); | ||
| 2134 | retrc = rc; | ||
| 2135 | |||
| 2136 | if (rc == H_RESOURCE) | ||
| 2137 | rc = ibmvmc_reset_crq_queue(adapter); | ||
| 2138 | |||
| 2139 | if (rc == 2) { | ||
| 2140 | dev_warn(adapter->dev, "Partner adapter not ready\n"); | ||
| 2141 | retrc = 0; | ||
| 2142 | } else if (rc != 0) { | ||
| 2143 | dev_err(adapter->dev, "Error %d opening adapter\n", rc); | ||
| 2144 | goto reg_crq_failed; | ||
| 2145 | } | ||
| 2146 | |||
| 2147 | queue->cur = 0; | ||
| 2148 | spin_lock_init(&queue->lock); | ||
| 2149 | |||
| 2150 | tasklet_init(&adapter->work_task, ibmvmc_task, (unsigned long)adapter); | ||
| 2151 | |||
| 2152 | if (request_irq(vdev->irq, | ||
| 2153 | ibmvmc_handle_event, | ||
| 2154 | 0, "ibmvmc", (void *)adapter) != 0) { | ||
| 2155 | dev_err(adapter->dev, "couldn't register irq 0x%x\n", | ||
| 2156 | vdev->irq); | ||
| 2157 | goto req_irq_failed; | ||
| 2158 | } | ||
| 2159 | |||
| 2160 | rc = vio_enable_interrupts(vdev); | ||
| 2161 | if (rc != 0) { | ||
| 2162 | dev_err(adapter->dev, "Error %d enabling interrupts!!!\n", rc); | ||
| 2163 | goto req_irq_failed; | ||
| 2164 | } | ||
| 2165 | |||
| 2166 | return retrc; | ||
| 2167 | |||
| 2168 | req_irq_failed: | ||
| 2169 | /* Cannot have any work since we either never got our IRQ registered, | ||
| 2170 | * or never got interrupts enabled | ||
| 2171 | */ | ||
| 2172 | tasklet_kill(&adapter->work_task); | ||
| 2173 | h_free_crq(vdev->unit_address); | ||
| 2174 | reg_crq_failed: | ||
| 2175 | dma_unmap_single(adapter->dev, | ||
| 2176 | queue->msg_token, | ||
| 2177 | queue->size * sizeof(*queue->msgs), DMA_BIDIRECTIONAL); | ||
| 2178 | map_failed: | ||
| 2179 | free_page((unsigned long)queue->msgs); | ||
| 2180 | malloc_failed: | ||
| 2181 | return -ENOMEM; | ||
| 2182 | } | ||
| 2183 | |||
| 2184 | /* Fill in the liobn and riobn fields on the adapter */ | ||
| 2185 | static int read_dma_window(struct vio_dev *vdev, | ||
| 2186 | struct crq_server_adapter *adapter) | ||
| 2187 | { | ||
| 2188 | const __be32 *dma_window; | ||
| 2189 | const __be32 *prop; | ||
| 2190 | |||
| 2191 | /* TODO Using of_parse_dma_window would be better, but it doesn't give | ||
| 2192 | * a way to read multiple windows without already knowing the size of | ||
| 2193 | * a window or the number of windows | ||
| 2194 | */ | ||
| 2195 | dma_window = | ||
| 2196 | (const __be32 *)vio_get_attribute(vdev, "ibm,my-dma-window", | ||
| 2197 | NULL); | ||
| 2198 | if (!dma_window) { | ||
| 2199 | dev_warn(adapter->dev, "Couldn't find ibm,my-dma-window property\n"); | ||
| 2200 | return -1; | ||
| 2201 | } | ||
| 2202 | |||
| 2203 | adapter->liobn = be32_to_cpu(*dma_window); | ||
| 2204 | dma_window++; | ||
| 2205 | |||
| 2206 | prop = (const __be32 *)vio_get_attribute(vdev, "ibm,#dma-address-cells", | ||
| 2207 | NULL); | ||
| 2208 | if (!prop) { | ||
| 2209 | dev_warn(adapter->dev, "Couldn't find ibm,#dma-address-cells property\n"); | ||
| 2210 | dma_window++; | ||
| 2211 | } else { | ||
| 2212 | dma_window += be32_to_cpu(*prop); | ||
| 2213 | } | ||
| 2214 | |||
| 2215 | prop = (const __be32 *)vio_get_attribute(vdev, "ibm,#dma-size-cells", | ||
| 2216 | NULL); | ||
| 2217 | if (!prop) { | ||
| 2218 | dev_warn(adapter->dev, "Couldn't find ibm,#dma-size-cells property\n"); | ||
| 2219 | dma_window++; | ||
| 2220 | } else { | ||
| 2221 | dma_window += be32_to_cpu(*prop); | ||
| 2222 | } | ||
| 2223 | |||
| 2224 | /* dma_window should point to the second window now */ | ||
| 2225 | adapter->riobn = be32_to_cpu(*dma_window); | ||
| 2226 | |||
| 2227 | return 0; | ||
| 2228 | } | ||
| 2229 | |||
| 2230 | static int ibmvmc_probe(struct vio_dev *vdev, const struct vio_device_id *id) | ||
| 2231 | { | ||
| 2232 | struct crq_server_adapter *adapter = &ibmvmc_adapter; | ||
| 2233 | int rc; | ||
| 2234 | |||
| 2235 | dev_set_drvdata(&vdev->dev, NULL); | ||
| 2236 | memset(adapter, 0, sizeof(*adapter)); | ||
| 2237 | adapter->dev = &vdev->dev; | ||
| 2238 | |||
| 2239 | dev_info(adapter->dev, "Probe for UA 0x%x\n", vdev->unit_address); | ||
| 2240 | |||
| 2241 | rc = read_dma_window(vdev, adapter); | ||
| 2242 | if (rc != 0) { | ||
| 2243 | ibmvmc.state = ibmvmc_state_failed; | ||
| 2244 | return -1; | ||
| 2245 | } | ||
| 2246 | |||
| 2247 | dev_dbg(adapter->dev, "Probe: liobn 0x%x, riobn 0x%x\n", | ||
| 2248 | adapter->liobn, adapter->riobn); | ||
| 2249 | |||
| 2250 | init_waitqueue_head(&adapter->reset_wait_queue); | ||
| 2251 | adapter->reset_task = kthread_run(ibmvmc_reset_task, adapter, "ibmvmc"); | ||
| 2252 | if (IS_ERR(adapter->reset_task)) { | ||
| 2253 | dev_err(adapter->dev, "Failed to start reset thread\n"); | ||
| 2254 | ibmvmc.state = ibmvmc_state_failed; | ||
| 2255 | rc = PTR_ERR(adapter->reset_task); | ||
| 2256 | adapter->reset_task = NULL; | ||
| 2257 | return rc; | ||
| 2258 | } | ||
| 2259 | |||
| 2260 | rc = ibmvmc_init_crq_queue(adapter); | ||
| 2261 | if (rc != 0 && rc != H_RESOURCE) { | ||
| 2262 | dev_err(adapter->dev, "Error initializing CRQ. rc = 0x%x\n", | ||
| 2263 | rc); | ||
| 2264 | ibmvmc.state = ibmvmc_state_failed; | ||
| 2265 | goto crq_failed; | ||
| 2266 | } | ||
| 2267 | |||
| 2268 | ibmvmc.state = ibmvmc_state_crqinit; | ||
| 2269 | |||
| 2270 | /* Try to send an initialization message. Note that this is allowed | ||
| 2271 | * to fail if the other end is not acive. In that case we just wait | ||
| 2272 | * for the other side to initialize. | ||
| 2273 | */ | ||
| 2274 | if (ibmvmc_send_crq(adapter, 0xC001000000000000LL, 0) != 0 && | ||
| 2275 | rc != H_RESOURCE) | ||
| 2276 | dev_warn(adapter->dev, "Failed to send initialize CRQ message\n"); | ||
| 2277 | |||
| 2278 | dev_set_drvdata(&vdev->dev, adapter); | ||
| 2279 | |||
| 2280 | return 0; | ||
| 2281 | |||
| 2282 | crq_failed: | ||
| 2283 | kthread_stop(adapter->reset_task); | ||
| 2284 | adapter->reset_task = NULL; | ||
| 2285 | return -EPERM; | ||
| 2286 | } | ||
| 2287 | |||
| 2288 | static int ibmvmc_remove(struct vio_dev *vdev) | ||
| 2289 | { | ||
| 2290 | struct crq_server_adapter *adapter = dev_get_drvdata(&vdev->dev); | ||
| 2291 | |||
| 2292 | dev_info(adapter->dev, "Entering remove for UA 0x%x\n", | ||
| 2293 | vdev->unit_address); | ||
| 2294 | ibmvmc_release_crq_queue(adapter); | ||
| 2295 | |||
| 2296 | return 0; | ||
| 2297 | } | ||
| 2298 | |||
| 2299 | static struct vio_device_id ibmvmc_device_table[] = { | ||
| 2300 | { "ibm,vmc", "IBM,vmc" }, | ||
| 2301 | { "", "" } | ||
| 2302 | }; | ||
| 2303 | MODULE_DEVICE_TABLE(vio, ibmvmc_device_table); | ||
| 2304 | |||
| 2305 | static struct vio_driver ibmvmc_driver = { | ||
| 2306 | .name = ibmvmc_driver_name, | ||
| 2307 | .id_table = ibmvmc_device_table, | ||
| 2308 | .probe = ibmvmc_probe, | ||
| 2309 | .remove = ibmvmc_remove, | ||
| 2310 | }; | ||
| 2311 | |||
| 2312 | static void __init ibmvmc_scrub_module_parms(void) | ||
| 2313 | { | ||
| 2314 | if (ibmvmc_max_mtu > MAX_MTU) { | ||
| 2315 | pr_warn("ibmvmc: Max MTU reduced to %d\n", MAX_MTU); | ||
| 2316 | ibmvmc_max_mtu = MAX_MTU; | ||
| 2317 | } else if (ibmvmc_max_mtu < MIN_MTU) { | ||
| 2318 | pr_warn("ibmvmc: Max MTU increased to %d\n", MIN_MTU); | ||
| 2319 | ibmvmc_max_mtu = MIN_MTU; | ||
| 2320 | } | ||
| 2321 | |||
| 2322 | if (ibmvmc_max_buf_pool_size > MAX_BUF_POOL_SIZE) { | ||
| 2323 | pr_warn("ibmvmc: Max buffer pool size reduced to %d\n", | ||
| 2324 | MAX_BUF_POOL_SIZE); | ||
| 2325 | ibmvmc_max_buf_pool_size = MAX_BUF_POOL_SIZE; | ||
| 2326 | } else if (ibmvmc_max_buf_pool_size < MIN_BUF_POOL_SIZE) { | ||
| 2327 | pr_warn("ibmvmc: Max buffer pool size increased to %d\n", | ||
| 2328 | MIN_BUF_POOL_SIZE); | ||
| 2329 | ibmvmc_max_buf_pool_size = MIN_BUF_POOL_SIZE; | ||
| 2330 | } | ||
| 2331 | |||
| 2332 | if (ibmvmc_max_hmcs > MAX_HMCS) { | ||
| 2333 | pr_warn("ibmvmc: Max HMCs reduced to %d\n", MAX_HMCS); | ||
| 2334 | ibmvmc_max_hmcs = MAX_HMCS; | ||
| 2335 | } else if (ibmvmc_max_hmcs < MIN_HMCS) { | ||
| 2336 | pr_warn("ibmvmc: Max HMCs increased to %d\n", MIN_HMCS); | ||
| 2337 | ibmvmc_max_hmcs = MIN_HMCS; | ||
| 2338 | } | ||
| 2339 | } | ||
| 2340 | |||
| 2341 | static struct miscdevice ibmvmc_miscdev = { | ||
| 2342 | .name = ibmvmc_driver_name, | ||
| 2343 | .minor = MISC_DYNAMIC_MINOR, | ||
| 2344 | .fops = &ibmvmc_fops, | ||
| 2345 | }; | ||
| 2346 | |||
| 2347 | static int __init ibmvmc_module_init(void) | ||
| 2348 | { | ||
| 2349 | int rc, i, j; | ||
| 2350 | |||
| 2351 | ibmvmc.state = ibmvmc_state_initial; | ||
| 2352 | pr_info("ibmvmc: version %s\n", IBMVMC_DRIVER_VERSION); | ||
| 2353 | |||
| 2354 | rc = misc_register(&ibmvmc_miscdev); | ||
| 2355 | if (rc) { | ||
| 2356 | pr_err("ibmvmc: misc registration failed\n"); | ||
| 2357 | goto misc_register_failed; | ||
| 2358 | } | ||
| 2359 | pr_info("ibmvmc: node %d:%d\n", MISC_MAJOR, | ||
| 2360 | ibmvmc_miscdev.minor); | ||
| 2361 | |||
| 2362 | /* Initialize data structures */ | ||
| 2363 | memset(hmcs, 0, sizeof(struct ibmvmc_hmc) * MAX_HMCS); | ||
| 2364 | for (i = 0; i < MAX_HMCS; i++) { | ||
| 2365 | spin_lock_init(&hmcs[i].lock); | ||
| 2366 | hmcs[i].state = ibmhmc_state_free; | ||
| 2367 | for (j = 0; j < MAX_BUF_POOL_SIZE; j++) | ||
| 2368 | hmcs[i].queue_outbound_msgs[j] = VMC_INVALID_BUFFER_ID; | ||
| 2369 | } | ||
| 2370 | |||
| 2371 | /* Sanity check module parms */ | ||
| 2372 | ibmvmc_scrub_module_parms(); | ||
| 2373 | |||
| 2374 | /* | ||
| 2375 | * Initialize some reasonable values. Might be negotiated smaller | ||
| 2376 | * values during the capabilities exchange. | ||
| 2377 | */ | ||
| 2378 | ibmvmc.max_mtu = ibmvmc_max_mtu; | ||
| 2379 | ibmvmc.max_buffer_pool_size = ibmvmc_max_buf_pool_size; | ||
| 2380 | ibmvmc.max_hmc_index = ibmvmc_max_hmcs - 1; | ||
| 2381 | |||
| 2382 | rc = vio_register_driver(&ibmvmc_driver); | ||
| 2383 | |||
| 2384 | if (rc) { | ||
| 2385 | pr_err("ibmvmc: rc %d from vio_register_driver\n", rc); | ||
| 2386 | goto vio_reg_failed; | ||
| 2387 | } | ||
| 2388 | |||
| 2389 | return 0; | ||
| 2390 | |||
| 2391 | vio_reg_failed: | ||
| 2392 | misc_deregister(&ibmvmc_miscdev); | ||
| 2393 | misc_register_failed: | ||
| 2394 | return rc; | ||
| 2395 | } | ||
| 2396 | |||
| 2397 | static void __exit ibmvmc_module_exit(void) | ||
| 2398 | { | ||
| 2399 | pr_info("ibmvmc: module exit\n"); | ||
| 2400 | vio_unregister_driver(&ibmvmc_driver); | ||
| 2401 | misc_deregister(&ibmvmc_miscdev); | ||
| 2402 | } | ||
| 2403 | |||
| 2404 | module_init(ibmvmc_module_init); | ||
| 2405 | module_exit(ibmvmc_module_exit); | ||
| 2406 | |||
| 2407 | module_param_named(buf_pool_size, ibmvmc_max_buf_pool_size, | ||
| 2408 | int, 0644); | ||
| 2409 | MODULE_PARM_DESC(buf_pool_size, "Buffer pool size"); | ||
| 2410 | module_param_named(max_hmcs, ibmvmc_max_hmcs, int, 0644); | ||
| 2411 | MODULE_PARM_DESC(max_hmcs, "Max HMCs"); | ||
| 2412 | module_param_named(max_mtu, ibmvmc_max_mtu, int, 0644); | ||
| 2413 | MODULE_PARM_DESC(max_mtu, "Max MTU"); | ||
| 2414 | |||
| 2415 | MODULE_AUTHOR("Steven Royer <seroyer@linux.vnet.ibm.com>"); | ||
| 2416 | MODULE_DESCRIPTION("IBM VMC"); | ||
| 2417 | MODULE_VERSION(IBMVMC_DRIVER_VERSION); | ||
| 2418 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/drivers/misc/ibmvmc.h b/drivers/misc/ibmvmc.h new file mode 100644 index 000000000000..e140ada8fe2c --- /dev/null +++ b/drivers/misc/ibmvmc.h | |||
| @@ -0,0 +1,209 @@ | |||
| 1 | /* SPDX-License-Identifier: GPL-2.0+ | ||
| 2 | * | ||
| 3 | * linux/drivers/misc/ibmvmc.h | ||
| 4 | * | ||
| 5 | * IBM Power Systems Virtual Management Channel Support. | ||
| 6 | * | ||
| 7 | * Copyright (c) 2004, 2018 IBM Corp. | ||
| 8 | * Dave Engebretsen engebret@us.ibm.com | ||
| 9 | * Steven Royer seroyer@linux.vnet.ibm.com | ||
| 10 | * Adam Reznechek adreznec@linux.vnet.ibm.com | ||
| 11 | * Bryant G. Ly <bryantly@linux.vnet.ibm.com> | ||
| 12 | */ | ||
| 13 | #ifndef IBMVMC_H | ||
| 14 | #define IBMVMC_H | ||
| 15 | |||
| 16 | #include <linux/types.h> | ||
| 17 | #include <linux/cdev.h> | ||
| 18 | |||
| 19 | #include <asm/vio.h> | ||
| 20 | |||
| 21 | #define IBMVMC_PROTOCOL_VERSION 0x0101 | ||
| 22 | |||
| 23 | #define MIN_BUF_POOL_SIZE 16 | ||
| 24 | #define MIN_HMCS 1 | ||
| 25 | #define MIN_MTU 4096 | ||
| 26 | #define MAX_BUF_POOL_SIZE 64 | ||
| 27 | #define MAX_HMCS 2 | ||
| 28 | #define MAX_MTU (4 * 4096) | ||
| 29 | #define DEFAULT_BUF_POOL_SIZE 32 | ||
| 30 | #define DEFAULT_HMCS 1 | ||
| 31 | #define DEFAULT_MTU 4096 | ||
| 32 | #define HMC_ID_LEN 32 | ||
| 33 | |||
| 34 | #define VMC_INVALID_BUFFER_ID 0xFFFF | ||
| 35 | |||
| 36 | /* ioctl numbers */ | ||
| 37 | #define VMC_BASE 0xCC | ||
| 38 | #define VMC_IOCTL_SETHMCID _IOW(VMC_BASE, 0x00, unsigned char *) | ||
| 39 | #define VMC_IOCTL_QUERY _IOR(VMC_BASE, 0x01, struct ibmvmc_query_struct) | ||
| 40 | #define VMC_IOCTL_REQUESTVMC _IOR(VMC_BASE, 0x02, u32) | ||
| 41 | |||
| 42 | #define VMC_MSG_CAP 0x01 | ||
| 43 | #define VMC_MSG_CAP_RESP 0x81 | ||
| 44 | #define VMC_MSG_OPEN 0x02 | ||
| 45 | #define VMC_MSG_OPEN_RESP 0x82 | ||
| 46 | #define VMC_MSG_CLOSE 0x03 | ||
| 47 | #define VMC_MSG_CLOSE_RESP 0x83 | ||
| 48 | #define VMC_MSG_ADD_BUF 0x04 | ||
| 49 | #define VMC_MSG_ADD_BUF_RESP 0x84 | ||
| 50 | #define VMC_MSG_REM_BUF 0x05 | ||
| 51 | #define VMC_MSG_REM_BUF_RESP 0x85 | ||
| 52 | #define VMC_MSG_SIGNAL 0x06 | ||
| 53 | |||
| 54 | #define VMC_MSG_SUCCESS 0 | ||
| 55 | #define VMC_MSG_INVALID_HMC_INDEX 1 | ||
| 56 | #define VMC_MSG_INVALID_BUFFER_ID 2 | ||
| 57 | #define VMC_MSG_CLOSED_HMC 3 | ||
| 58 | #define VMC_MSG_INTERFACE_FAILURE 4 | ||
| 59 | #define VMC_MSG_NO_BUFFER 5 | ||
| 60 | |||
| 61 | #define VMC_BUF_OWNER_ALPHA 0 | ||
| 62 | #define VMC_BUF_OWNER_HV 1 | ||
| 63 | |||
| 64 | enum ibmvmc_states { | ||
| 65 | ibmvmc_state_sched_reset = -1, | ||
| 66 | ibmvmc_state_initial = 0, | ||
| 67 | ibmvmc_state_crqinit = 1, | ||
| 68 | ibmvmc_state_capabilities = 2, | ||
| 69 | ibmvmc_state_ready = 3, | ||
| 70 | ibmvmc_state_failed = 4, | ||
| 71 | }; | ||
| 72 | |||
| 73 | enum ibmhmc_states { | ||
| 74 | /* HMC connection not established */ | ||
| 75 | ibmhmc_state_free = 0, | ||
| 76 | |||
| 77 | /* HMC connection established (open called) */ | ||
| 78 | ibmhmc_state_initial = 1, | ||
| 79 | |||
| 80 | /* open msg sent to HV, due to ioctl(1) call */ | ||
| 81 | ibmhmc_state_opening = 2, | ||
| 82 | |||
| 83 | /* HMC connection ready, open resp msg from HV */ | ||
| 84 | ibmhmc_state_ready = 3, | ||
| 85 | |||
| 86 | /* HMC connection failure */ | ||
| 87 | ibmhmc_state_failed = 4, | ||
| 88 | }; | ||
| 89 | |||
| 90 | struct ibmvmc_buffer { | ||
| 91 | u8 valid; /* 1 when DMA storage allocated to buffer */ | ||
| 92 | u8 free; /* 1 when buffer available for the Alpha Partition */ | ||
| 93 | u8 owner; | ||
| 94 | u16 id; | ||
| 95 | u32 size; | ||
| 96 | u32 msg_len; | ||
| 97 | dma_addr_t dma_addr_local; | ||
| 98 | dma_addr_t dma_addr_remote; | ||
| 99 | void *real_addr_local; | ||
| 100 | }; | ||
| 101 | |||
| 102 | struct ibmvmc_admin_crq_msg { | ||
| 103 | u8 valid; /* RPA Defined */ | ||
| 104 | u8 type; /* ibmvmc msg type */ | ||
| 105 | u8 status; /* Response msg status. Zero is success and on failure, | ||
| 106 | * either 1 - General Failure, or 2 - Invalid Version is | ||
| 107 | * returned. | ||
| 108 | */ | ||
| 109 | u8 rsvd[2]; | ||
| 110 | u8 max_hmc; /* Max # of independent HMC connections supported */ | ||
| 111 | __be16 pool_size; /* Maximum number of buffers supported per HMC | ||
| 112 | * connection | ||
| 113 | */ | ||
| 114 | __be32 max_mtu; /* Maximum message size supported (bytes) */ | ||
| 115 | __be16 crq_size; /* # of entries available in the CRQ for the | ||
| 116 | * source partition. The target partition must | ||
| 117 | * limit the number of outstanding messages to | ||
| 118 | * one half or less. | ||
| 119 | */ | ||
| 120 | __be16 version; /* Indicates the code level of the management partition | ||
| 121 | * or the hypervisor with the high-order byte | ||
| 122 | * indicating a major version and the low-order byte | ||
| 123 | * indicating a minor version. | ||
| 124 | */ | ||
| 125 | }; | ||
| 126 | |||
| 127 | struct ibmvmc_crq_msg { | ||
| 128 | u8 valid; /* RPA Defined */ | ||
| 129 | u8 type; /* ibmvmc msg type */ | ||
| 130 | u8 status; /* Response msg status */ | ||
| 131 | union { | ||
| 132 | u8 rsvd; /* Reserved */ | ||
| 133 | u8 owner; | ||
| 134 | } var1; | ||
| 135 | u8 hmc_session; /* Session Identifier for the current VMC connection */ | ||
| 136 | u8 hmc_index; /* A unique HMC Idx would be used if multiple management | ||
| 137 | * applications running concurrently were desired | ||
| 138 | */ | ||
| 139 | union { | ||
| 140 | __be16 rsvd; | ||
| 141 | __be16 buffer_id; | ||
| 142 | } var2; | ||
| 143 | __be32 rsvd; | ||
| 144 | union { | ||
| 145 | __be32 rsvd; | ||
| 146 | __be32 lioba; | ||
| 147 | __be32 msg_len; | ||
| 148 | } var3; | ||
| 149 | }; | ||
| 150 | |||
| 151 | /* an RPA command/response transport queue */ | ||
| 152 | struct crq_queue { | ||
| 153 | struct ibmvmc_crq_msg *msgs; | ||
| 154 | int size, cur; | ||
| 155 | dma_addr_t msg_token; | ||
| 156 | spinlock_t lock; | ||
| 157 | }; | ||
| 158 | |||
| 159 | /* VMC server adapter settings */ | ||
| 160 | struct crq_server_adapter { | ||
| 161 | struct device *dev; | ||
| 162 | struct crq_queue queue; | ||
| 163 | u32 liobn; | ||
| 164 | u32 riobn; | ||
| 165 | struct tasklet_struct work_task; | ||
| 166 | wait_queue_head_t reset_wait_queue; | ||
| 167 | struct task_struct *reset_task; | ||
| 168 | }; | ||
| 169 | |||
| 170 | /* Driver wide settings */ | ||
| 171 | struct ibmvmc_struct { | ||
| 172 | u32 state; | ||
| 173 | u32 max_mtu; | ||
| 174 | u32 max_buffer_pool_size; | ||
| 175 | u32 max_hmc_index; | ||
| 176 | struct crq_server_adapter *adapter; | ||
| 177 | struct cdev cdev; | ||
| 178 | u32 vmc_drc_index; | ||
| 179 | }; | ||
| 180 | |||
| 181 | struct ibmvmc_file_session; | ||
| 182 | |||
| 183 | /* Connection specific settings */ | ||
| 184 | struct ibmvmc_hmc { | ||
| 185 | u8 session; | ||
| 186 | u8 index; | ||
| 187 | u32 state; | ||
| 188 | struct crq_server_adapter *adapter; | ||
| 189 | spinlock_t lock; | ||
| 190 | unsigned char hmc_id[HMC_ID_LEN]; | ||
| 191 | struct ibmvmc_buffer buffer[MAX_BUF_POOL_SIZE]; | ||
| 192 | unsigned short queue_outbound_msgs[MAX_BUF_POOL_SIZE]; | ||
| 193 | int queue_head, queue_tail; | ||
| 194 | struct ibmvmc_file_session *file_session; | ||
| 195 | }; | ||
| 196 | |||
| 197 | struct ibmvmc_file_session { | ||
| 198 | struct file *file; | ||
| 199 | struct ibmvmc_hmc *hmc; | ||
| 200 | bool valid; | ||
| 201 | }; | ||
| 202 | |||
| 203 | struct ibmvmc_query_struct { | ||
| 204 | int have_vmc; | ||
| 205 | int state; | ||
| 206 | int vmc_drc_index; | ||
| 207 | }; | ||
| 208 | |||
| 209 | #endif /* __IBMVMC_H */ | ||
diff --git a/drivers/misc/sgi-gru/grumain.c b/drivers/misc/sgi-gru/grumain.c index 3641f1334cf0..ab174f28e3be 100644 --- a/drivers/misc/sgi-gru/grumain.c +++ b/drivers/misc/sgi-gru/grumain.c | |||
| @@ -926,7 +926,7 @@ again: | |||
| 926 | * | 926 | * |
| 927 | * Note: gru segments alway mmaped on GRU_GSEG_PAGESIZE boundaries. | 927 | * Note: gru segments alway mmaped on GRU_GSEG_PAGESIZE boundaries. |
| 928 | */ | 928 | */ |
| 929 | int gru_fault(struct vm_fault *vmf) | 929 | vm_fault_t gru_fault(struct vm_fault *vmf) |
| 930 | { | 930 | { |
| 931 | struct vm_area_struct *vma = vmf->vma; | 931 | struct vm_area_struct *vma = vmf->vma; |
| 932 | struct gru_thread_state *gts; | 932 | struct gru_thread_state *gts; |
diff --git a/drivers/misc/sgi-gru/grutables.h b/drivers/misc/sgi-gru/grutables.h index b5e308b50ed1..3e041b6f7a68 100644 --- a/drivers/misc/sgi-gru/grutables.h +++ b/drivers/misc/sgi-gru/grutables.h | |||
| @@ -147,6 +147,7 @@ | |||
| 147 | #include <linux/mutex.h> | 147 | #include <linux/mutex.h> |
| 148 | #include <linux/wait.h> | 148 | #include <linux/wait.h> |
| 149 | #include <linux/mmu_notifier.h> | 149 | #include <linux/mmu_notifier.h> |
| 150 | #include <linux/mm_types.h> | ||
| 150 | #include "gru.h" | 151 | #include "gru.h" |
| 151 | #include "grulib.h" | 152 | #include "grulib.h" |
| 152 | #include "gruhandles.h" | 153 | #include "gruhandles.h" |
| @@ -665,7 +666,7 @@ extern unsigned long gru_reserve_cb_resources(struct gru_state *gru, | |||
| 665 | int cbr_au_count, char *cbmap); | 666 | int cbr_au_count, char *cbmap); |
| 666 | extern unsigned long gru_reserve_ds_resources(struct gru_state *gru, | 667 | extern unsigned long gru_reserve_ds_resources(struct gru_state *gru, |
| 667 | int dsr_au_count, char *dsmap); | 668 | int dsr_au_count, char *dsmap); |
| 668 | extern int gru_fault(struct vm_fault *vmf); | 669 | extern vm_fault_t gru_fault(struct vm_fault *vmf); |
| 669 | extern struct gru_mm_struct *gru_register_mmu_notifier(void); | 670 | extern struct gru_mm_struct *gru_register_mmu_notifier(void); |
| 670 | extern void gru_drop_mmu_notifier(struct gru_mm_struct *gms); | 671 | extern void gru_drop_mmu_notifier(struct gru_mm_struct *gms); |
| 671 | 672 | ||
diff --git a/drivers/misc/sgi-xp/xpnet.c b/drivers/misc/sgi-xp/xpnet.c index 0c26eaf5f62b..216d5c756236 100644 --- a/drivers/misc/sgi-xp/xpnet.c +++ b/drivers/misc/sgi-xp/xpnet.c | |||
| @@ -407,7 +407,7 @@ xpnet_send(struct sk_buff *skb, struct xpnet_pending_msg *queued_msg, | |||
| 407 | * destination partid. If the destination partid octets are 0xffff, | 407 | * destination partid. If the destination partid octets are 0xffff, |
| 408 | * this packet is to be broadcast to all connected partitions. | 408 | * this packet is to be broadcast to all connected partitions. |
| 409 | */ | 409 | */ |
| 410 | static int | 410 | static netdev_tx_t |
| 411 | xpnet_dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) | 411 | xpnet_dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) |
| 412 | { | 412 | { |
| 413 | struct xpnet_pending_msg *queued_msg; | 413 | struct xpnet_pending_msg *queued_msg; |
diff --git a/drivers/misc/ti-st/st_kim.c b/drivers/misc/ti-st/st_kim.c index b77aacafc3fc..5ec3f5a43718 100644 --- a/drivers/misc/ti-st/st_kim.c +++ b/drivers/misc/ti-st/st_kim.c | |||
| @@ -735,7 +735,7 @@ static int kim_probe(struct platform_device *pdev) | |||
| 735 | st_kim_devices[0] = pdev; | 735 | st_kim_devices[0] = pdev; |
| 736 | } | 736 | } |
| 737 | 737 | ||
| 738 | kim_gdata = kzalloc(sizeof(struct kim_data_s), GFP_ATOMIC); | 738 | kim_gdata = kzalloc(sizeof(struct kim_data_s), GFP_KERNEL); |
| 739 | if (!kim_gdata) { | 739 | if (!kim_gdata) { |
| 740 | pr_err("no mem to allocate"); | 740 | pr_err("no mem to allocate"); |
| 741 | return -ENOMEM; | 741 | return -ENOMEM; |
diff --git a/drivers/misc/tifm_7xx1.c b/drivers/misc/tifm_7xx1.c index e5f108713dd8..9ac95b48ef92 100644 --- a/drivers/misc/tifm_7xx1.c +++ b/drivers/misc/tifm_7xx1.c | |||
| @@ -239,9 +239,13 @@ static int tifm_7xx1_resume(struct pci_dev *dev) | |||
| 239 | unsigned long timeout; | 239 | unsigned long timeout; |
| 240 | unsigned int good_sockets = 0, bad_sockets = 0; | 240 | unsigned int good_sockets = 0, bad_sockets = 0; |
| 241 | unsigned long flags; | 241 | unsigned long flags; |
| 242 | unsigned char new_ids[fm->num_sockets]; | 242 | /* Maximum number of entries is 4 */ |
| 243 | unsigned char new_ids[4]; | ||
| 243 | DECLARE_COMPLETION_ONSTACK(finish_resume); | 244 | DECLARE_COMPLETION_ONSTACK(finish_resume); |
| 244 | 245 | ||
| 246 | if (WARN_ON(fm->num_sockets > ARRAY_SIZE(new_ids))) | ||
| 247 | return -ENXIO; | ||
| 248 | |||
| 245 | pci_set_power_state(dev, PCI_D0); | 249 | pci_set_power_state(dev, PCI_D0); |
| 246 | pci_restore_state(dev); | 250 | pci_restore_state(dev); |
| 247 | rc = pci_enable_device(dev); | 251 | rc = pci_enable_device(dev); |
diff --git a/drivers/misc/vmw_balloon.c b/drivers/misc/vmw_balloon.c index 9047c0a529b2..efd733472a35 100644 --- a/drivers/misc/vmw_balloon.c +++ b/drivers/misc/vmw_balloon.c | |||
| @@ -576,15 +576,9 @@ static void vmballoon_pop(struct vmballoon *b) | |||
| 576 | } | 576 | } |
| 577 | } | 577 | } |
| 578 | 578 | ||
| 579 | if (b->batch_page) { | 579 | /* Clearing the batch_page unconditionally has no adverse effect */ |
| 580 | vunmap(b->batch_page); | 580 | free_page((unsigned long)b->batch_page); |
| 581 | b->batch_page = NULL; | 581 | b->batch_page = NULL; |
| 582 | } | ||
| 583 | |||
| 584 | if (b->page) { | ||
| 585 | __free_page(b->page); | ||
| 586 | b->page = NULL; | ||
| 587 | } | ||
| 588 | } | 582 | } |
| 589 | 583 | ||
| 590 | /* | 584 | /* |
| @@ -991,16 +985,13 @@ static const struct vmballoon_ops vmballoon_batched_ops = { | |||
| 991 | 985 | ||
| 992 | static bool vmballoon_init_batching(struct vmballoon *b) | 986 | static bool vmballoon_init_batching(struct vmballoon *b) |
| 993 | { | 987 | { |
| 994 | b->page = alloc_page(VMW_PAGE_ALLOC_NOSLEEP); | 988 | struct page *page; |
| 995 | if (!b->page) | ||
| 996 | return false; | ||
| 997 | 989 | ||
| 998 | b->batch_page = vmap(&b->page, 1, VM_MAP, PAGE_KERNEL); | 990 | page = alloc_page(GFP_KERNEL | __GFP_ZERO); |
| 999 | if (!b->batch_page) { | 991 | if (!page) |
| 1000 | __free_page(b->page); | ||
| 1001 | return false; | 992 | return false; |
| 1002 | } | ||
| 1003 | 993 | ||
| 994 | b->batch_page = page_address(page); | ||
| 1004 | return true; | 995 | return true; |
| 1005 | } | 996 | } |
| 1006 | 997 | ||
diff --git a/drivers/mux/adg792a.c b/drivers/mux/adg792a.c index 6a8725cf3d71..e8fc2fc1ab09 100644 --- a/drivers/mux/adg792a.c +++ b/drivers/mux/adg792a.c | |||
| @@ -58,8 +58,7 @@ static const struct mux_control_ops adg792a_ops = { | |||
| 58 | .set = adg792a_set, | 58 | .set = adg792a_set, |
| 59 | }; | 59 | }; |
| 60 | 60 | ||
| 61 | static int adg792a_probe(struct i2c_client *i2c, | 61 | static int adg792a_probe(struct i2c_client *i2c) |
| 62 | const struct i2c_device_id *id) | ||
| 63 | { | 62 | { |
| 64 | struct device *dev = &i2c->dev; | 63 | struct device *dev = &i2c->dev; |
| 65 | struct mux_chip *mux_chip; | 64 | struct mux_chip *mux_chip; |
| @@ -144,7 +143,7 @@ static struct i2c_driver adg792a_driver = { | |||
| 144 | .name = "adg792a", | 143 | .name = "adg792a", |
| 145 | .of_match_table = of_match_ptr(adg792a_of_match), | 144 | .of_match_table = of_match_ptr(adg792a_of_match), |
| 146 | }, | 145 | }, |
| 147 | .probe = adg792a_probe, | 146 | .probe_new = adg792a_probe, |
| 148 | .id_table = adg792a_id, | 147 | .id_table = adg792a_id, |
| 149 | }; | 148 | }; |
| 150 | module_i2c_driver(adg792a_driver); | 149 | module_i2c_driver(adg792a_driver); |
diff --git a/drivers/nubus/bus.c b/drivers/nubus/bus.c index d306c348c857..a59b6c4bb5b8 100644 --- a/drivers/nubus/bus.c +++ b/drivers/nubus/bus.c | |||
| @@ -63,20 +63,15 @@ static struct device nubus_parent = { | |||
| 63 | .init_name = "nubus", | 63 | .init_name = "nubus", |
| 64 | }; | 64 | }; |
| 65 | 65 | ||
| 66 | int __init nubus_bus_register(void) | 66 | static int __init nubus_bus_register(void) |
| 67 | { | 67 | { |
| 68 | int err; | 68 | return bus_register(&nubus_bus_type); |
| 69 | 69 | } | |
| 70 | err = device_register(&nubus_parent); | 70 | postcore_initcall(nubus_bus_register); |
| 71 | if (err) | ||
| 72 | return err; | ||
| 73 | |||
| 74 | err = bus_register(&nubus_bus_type); | ||
| 75 | if (!err) | ||
| 76 | return 0; | ||
| 77 | 71 | ||
| 78 | device_unregister(&nubus_parent); | 72 | int __init nubus_parent_device_register(void) |
| 79 | return err; | 73 | { |
| 74 | return device_register(&nubus_parent); | ||
| 80 | } | 75 | } |
| 81 | 76 | ||
| 82 | static void nubus_device_release(struct device *dev) | 77 | static void nubus_device_release(struct device *dev) |
diff --git a/drivers/nubus/nubus.c b/drivers/nubus/nubus.c index 4621ff98138c..bb0d63a44f41 100644 --- a/drivers/nubus/nubus.c +++ b/drivers/nubus/nubus.c | |||
| @@ -875,7 +875,7 @@ static int __init nubus_init(void) | |||
| 875 | return 0; | 875 | return 0; |
| 876 | 876 | ||
| 877 | nubus_proc_init(); | 877 | nubus_proc_init(); |
| 878 | err = nubus_bus_register(); | 878 | err = nubus_parent_device_register(); |
| 879 | if (err) | 879 | if (err) |
| 880 | return err; | 880 | return err; |
| 881 | nubus_scan_bus(); | 881 | nubus_scan_bus(); |
diff --git a/drivers/nvmem/Kconfig b/drivers/nvmem/Kconfig index 1090924efdb1..54a3c298247b 100644 --- a/drivers/nvmem/Kconfig +++ b/drivers/nvmem/Kconfig | |||
| @@ -175,4 +175,10 @@ config NVMEM_SNVS_LPGPR | |||
| 175 | This driver can also be built as a module. If so, the module | 175 | This driver can also be built as a module. If so, the module |
| 176 | will be called nvmem-snvs-lpgpr. | 176 | will be called nvmem-snvs-lpgpr. |
| 177 | 177 | ||
| 178 | config RAVE_SP_EEPROM | ||
| 179 | tristate "Rave SP EEPROM Support" | ||
| 180 | depends on RAVE_SP_CORE | ||
| 181 | help | ||
| 182 | Say y here to enable Rave SP EEPROM support. | ||
| 183 | |||
| 178 | endif | 184 | endif |
diff --git a/drivers/nvmem/Makefile b/drivers/nvmem/Makefile index e54dcfa6565a..27e96a8efd1c 100644 --- a/drivers/nvmem/Makefile +++ b/drivers/nvmem/Makefile | |||
| @@ -37,3 +37,6 @@ obj-$(CONFIG_MESON_MX_EFUSE) += nvmem_meson_mx_efuse.o | |||
| 37 | nvmem_meson_mx_efuse-y := meson-mx-efuse.o | 37 | nvmem_meson_mx_efuse-y := meson-mx-efuse.o |
| 38 | obj-$(CONFIG_NVMEM_SNVS_LPGPR) += nvmem_snvs_lpgpr.o | 38 | obj-$(CONFIG_NVMEM_SNVS_LPGPR) += nvmem_snvs_lpgpr.o |
| 39 | nvmem_snvs_lpgpr-y := snvs_lpgpr.o | 39 | nvmem_snvs_lpgpr-y := snvs_lpgpr.o |
| 40 | obj-$(CONFIG_RAVE_SP_EEPROM) += nvmem-rave-sp-eeprom.o | ||
| 41 | nvmem-rave-sp-eeprom-y := rave-sp-eeprom.o | ||
| 42 | |||
diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c index b05aa8e81303..b5b0cdc21d01 100644 --- a/drivers/nvmem/core.c +++ b/drivers/nvmem/core.c | |||
| @@ -353,18 +353,27 @@ static int nvmem_cell_info_to_nvmem_cell(struct nvmem_device *nvmem, | |||
| 353 | return 0; | 353 | return 0; |
| 354 | } | 354 | } |
| 355 | 355 | ||
| 356 | static int nvmem_add_cells(struct nvmem_device *nvmem, | 356 | /** |
| 357 | const struct nvmem_config *cfg) | 357 | * nvmem_add_cells() - Add cell information to an nvmem device |
| 358 | * | ||
| 359 | * @nvmem: nvmem device to add cells to. | ||
| 360 | * @info: nvmem cell info to add to the device | ||
| 361 | * @ncells: number of cells in info | ||
| 362 | * | ||
| 363 | * Return: 0 or negative error code on failure. | ||
| 364 | */ | ||
| 365 | int nvmem_add_cells(struct nvmem_device *nvmem, | ||
| 366 | const struct nvmem_cell_info *info, | ||
| 367 | int ncells) | ||
| 358 | { | 368 | { |
| 359 | struct nvmem_cell **cells; | 369 | struct nvmem_cell **cells; |
| 360 | const struct nvmem_cell_info *info = cfg->cells; | ||
| 361 | int i, rval; | 370 | int i, rval; |
| 362 | 371 | ||
| 363 | cells = kcalloc(cfg->ncells, sizeof(*cells), GFP_KERNEL); | 372 | cells = kcalloc(ncells, sizeof(*cells), GFP_KERNEL); |
| 364 | if (!cells) | 373 | if (!cells) |
| 365 | return -ENOMEM; | 374 | return -ENOMEM; |
| 366 | 375 | ||
| 367 | for (i = 0; i < cfg->ncells; i++) { | 376 | for (i = 0; i < ncells; i++) { |
| 368 | cells[i] = kzalloc(sizeof(**cells), GFP_KERNEL); | 377 | cells[i] = kzalloc(sizeof(**cells), GFP_KERNEL); |
| 369 | if (!cells[i]) { | 378 | if (!cells[i]) { |
| 370 | rval = -ENOMEM; | 379 | rval = -ENOMEM; |
| @@ -380,7 +389,7 @@ static int nvmem_add_cells(struct nvmem_device *nvmem, | |||
| 380 | nvmem_cell_add(cells[i]); | 389 | nvmem_cell_add(cells[i]); |
| 381 | } | 390 | } |
| 382 | 391 | ||
| 383 | nvmem->ncells = cfg->ncells; | 392 | nvmem->ncells = ncells; |
| 384 | /* remove tmp array */ | 393 | /* remove tmp array */ |
| 385 | kfree(cells); | 394 | kfree(cells); |
| 386 | 395 | ||
| @@ -393,6 +402,7 @@ err: | |||
| 393 | 402 | ||
| 394 | return rval; | 403 | return rval; |
| 395 | } | 404 | } |
| 405 | EXPORT_SYMBOL_GPL(nvmem_add_cells); | ||
| 396 | 406 | ||
| 397 | /* | 407 | /* |
| 398 | * nvmem_setup_compat() - Create an additional binary entry in | 408 | * nvmem_setup_compat() - Create an additional binary entry in |
| @@ -509,7 +519,7 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config) | |||
| 509 | } | 519 | } |
| 510 | 520 | ||
| 511 | if (config->cells) | 521 | if (config->cells) |
| 512 | nvmem_add_cells(nvmem, config); | 522 | nvmem_add_cells(nvmem, config->cells, config->ncells); |
| 513 | 523 | ||
| 514 | return nvmem; | 524 | return nvmem; |
| 515 | 525 | ||
| @@ -559,6 +569,7 @@ static void devm_nvmem_release(struct device *dev, void *res) | |||
| 559 | * nvmem_config. | 569 | * nvmem_config. |
| 560 | * Also creates an binary entry in /sys/bus/nvmem/devices/dev-name/nvmem | 570 | * Also creates an binary entry in /sys/bus/nvmem/devices/dev-name/nvmem |
| 561 | * | 571 | * |
| 572 | * @dev: Device that uses the nvmem device. | ||
| 562 | * @config: nvmem device configuration with which nvmem device is created. | 573 | * @config: nvmem device configuration with which nvmem device is created. |
| 563 | * | 574 | * |
| 564 | * Return: Will be an ERR_PTR() on error or a valid pointer to nvmem_device | 575 | * 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) | |||
| 597 | * devm_nvmem_unregister() - Unregister previously registered managed nvmem | 608 | * devm_nvmem_unregister() - Unregister previously registered managed nvmem |
| 598 | * device. | 609 | * device. |
| 599 | * | 610 | * |
| 611 | * @dev: Device that uses the nvmem device. | ||
| 600 | * @nvmem: Pointer to previously registered nvmem device. | 612 | * @nvmem: Pointer to previously registered nvmem device. |
| 601 | * | 613 | * |
| 602 | * Return: Will be an negative on error or a zero on success. | 614 | * 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, | |||
| 1107 | 1119 | ||
| 1108 | /* setup the first byte with lsb bits from nvmem */ | 1120 | /* setup the first byte with lsb bits from nvmem */ |
| 1109 | rc = nvmem_reg_read(nvmem, cell->offset, &v, 1); | 1121 | rc = nvmem_reg_read(nvmem, cell->offset, &v, 1); |
| 1122 | if (rc) | ||
| 1123 | goto err; | ||
| 1110 | *b++ |= GENMASK(bit_offset - 1, 0) & v; | 1124 | *b++ |= GENMASK(bit_offset - 1, 0) & v; |
| 1111 | 1125 | ||
| 1112 | /* setup rest of the byte if any */ | 1126 | /* setup rest of the byte if any */ |
| @@ -1125,11 +1139,16 @@ static void *nvmem_cell_prepare_write_buffer(struct nvmem_cell *cell, | |||
| 1125 | /* setup the last byte with msb bits from nvmem */ | 1139 | /* setup the last byte with msb bits from nvmem */ |
| 1126 | rc = nvmem_reg_read(nvmem, | 1140 | rc = nvmem_reg_read(nvmem, |
| 1127 | cell->offset + cell->bytes - 1, &v, 1); | 1141 | cell->offset + cell->bytes - 1, &v, 1); |
| 1142 | if (rc) | ||
| 1143 | goto err; | ||
| 1128 | *p |= GENMASK(7, (nbits + bit_offset) % BITS_PER_BYTE) & v; | 1144 | *p |= GENMASK(7, (nbits + bit_offset) % BITS_PER_BYTE) & v; |
| 1129 | 1145 | ||
| 1130 | } | 1146 | } |
| 1131 | 1147 | ||
| 1132 | return buf; | 1148 | return buf; |
| 1149 | err: | ||
| 1150 | kfree(buf); | ||
| 1151 | return ERR_PTR(rc); | ||
| 1133 | } | 1152 | } |
| 1134 | 1153 | ||
| 1135 | /** | 1154 | /** |
diff --git a/drivers/nvmem/meson-efuse.c b/drivers/nvmem/meson-efuse.c index 71823d1403c5..d769840d1e18 100644 --- a/drivers/nvmem/meson-efuse.c +++ b/drivers/nvmem/meson-efuse.c | |||
| @@ -24,23 +24,16 @@ | |||
| 24 | static int meson_efuse_read(void *context, unsigned int offset, | 24 | static int meson_efuse_read(void *context, unsigned int offset, |
| 25 | void *val, size_t bytes) | 25 | void *val, size_t bytes) |
| 26 | { | 26 | { |
| 27 | u8 *buf = val; | 27 | return meson_sm_call_read((u8 *)val, bytes, SM_EFUSE_READ, offset, |
| 28 | int ret; | 28 | bytes, 0, 0, 0); |
| 29 | |||
| 30 | ret = meson_sm_call_read(buf, bytes, SM_EFUSE_READ, offset, | ||
| 31 | bytes, 0, 0, 0); | ||
| 32 | if (ret < 0) | ||
| 33 | return ret; | ||
| 34 | |||
| 35 | return 0; | ||
| 36 | } | 29 | } |
| 37 | 30 | ||
| 38 | static struct nvmem_config econfig = { | 31 | static int meson_efuse_write(void *context, unsigned int offset, |
| 39 | .name = "meson-efuse", | 32 | void *val, size_t bytes) |
| 40 | .stride = 1, | 33 | { |
| 41 | .word_size = 1, | 34 | return meson_sm_call_write((u8 *)val, bytes, SM_EFUSE_WRITE, offset, |
| 42 | .read_only = true, | 35 | bytes, 0, 0, 0); |
| 43 | }; | 36 | } |
| 44 | 37 | ||
| 45 | static const struct of_device_id meson_efuse_match[] = { | 38 | static const struct of_device_id meson_efuse_match[] = { |
| 46 | { .compatible = "amlogic,meson-gxbb-efuse", }, | 39 | { .compatible = "amlogic,meson-gxbb-efuse", }, |
| @@ -50,17 +43,27 @@ MODULE_DEVICE_TABLE(of, meson_efuse_match); | |||
| 50 | 43 | ||
| 51 | static int meson_efuse_probe(struct platform_device *pdev) | 44 | static int meson_efuse_probe(struct platform_device *pdev) |
| 52 | { | 45 | { |
| 46 | struct device *dev = &pdev->dev; | ||
| 53 | struct nvmem_device *nvmem; | 47 | struct nvmem_device *nvmem; |
| 48 | struct nvmem_config *econfig; | ||
| 54 | unsigned int size; | 49 | unsigned int size; |
| 55 | 50 | ||
| 56 | if (meson_sm_call(SM_EFUSE_USER_MAX, &size, 0, 0, 0, 0, 0) < 0) | 51 | if (meson_sm_call(SM_EFUSE_USER_MAX, &size, 0, 0, 0, 0, 0) < 0) |
| 57 | return -EINVAL; | 52 | return -EINVAL; |
| 58 | 53 | ||
| 59 | econfig.dev = &pdev->dev; | 54 | econfig = devm_kzalloc(dev, sizeof(*econfig), GFP_KERNEL); |
| 60 | econfig.reg_read = meson_efuse_read; | 55 | if (!econfig) |
| 61 | econfig.size = size; | 56 | return -ENOMEM; |
| 57 | |||
| 58 | econfig->dev = dev; | ||
| 59 | econfig->name = dev_name(dev); | ||
| 60 | econfig->stride = 1; | ||
| 61 | econfig->word_size = 1; | ||
| 62 | econfig->reg_read = meson_efuse_read; | ||
| 63 | econfig->reg_write = meson_efuse_write; | ||
| 64 | econfig->size = size; | ||
| 62 | 65 | ||
| 63 | nvmem = devm_nvmem_register(&pdev->dev, &econfig); | 66 | nvmem = devm_nvmem_register(&pdev->dev, econfig); |
| 64 | 67 | ||
| 65 | return PTR_ERR_OR_ZERO(nvmem); | 68 | return PTR_ERR_OR_ZERO(nvmem); |
| 66 | } | 69 | } |
diff --git a/drivers/nvmem/rave-sp-eeprom.c b/drivers/nvmem/rave-sp-eeprom.c new file mode 100644 index 000000000000..50aeea6ec6cc --- /dev/null +++ b/drivers/nvmem/rave-sp-eeprom.c | |||
| @@ -0,0 +1,357 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0+ | ||
| 2 | |||
| 3 | /* | ||
| 4 | * EEPROM driver for RAVE SP | ||
| 5 | * | ||
| 6 | * Copyright (C) 2018 Zodiac Inflight Innovations | ||
| 7 | * | ||
| 8 | */ | ||
| 9 | #include <linux/kernel.h> | ||
| 10 | #include <linux/mfd/rave-sp.h> | ||
| 11 | #include <linux/module.h> | ||
| 12 | #include <linux/nvmem-provider.h> | ||
| 13 | #include <linux/of_device.h> | ||
| 14 | #include <linux/platform_device.h> | ||
| 15 | #include <linux/sizes.h> | ||
| 16 | |||
| 17 | /** | ||
| 18 | * enum rave_sp_eeprom_access_type - Supported types of EEPROM access | ||
| 19 | * | ||
| 20 | * @RAVE_SP_EEPROM_WRITE: EEPROM write | ||
| 21 | * @RAVE_SP_EEPROM_READ: EEPROM read | ||
| 22 | */ | ||
| 23 | enum rave_sp_eeprom_access_type { | ||
| 24 | RAVE_SP_EEPROM_WRITE = 0, | ||
| 25 | RAVE_SP_EEPROM_READ = 1, | ||
| 26 | }; | ||
| 27 | |||
| 28 | /** | ||
| 29 | * enum rave_sp_eeprom_header_size - EEPROM command header sizes | ||
| 30 | * | ||
| 31 | * @RAVE_SP_EEPROM_HEADER_SMALL: EEPROM header size for "small" devices (< 8K) | ||
| 32 | * @RAVE_SP_EEPROM_HEADER_BIG: EEPROM header size for "big" devices (> 8K) | ||
| 33 | */ | ||
| 34 | enum rave_sp_eeprom_header_size { | ||
| 35 | RAVE_SP_EEPROM_HEADER_SMALL = 4U, | ||
| 36 | RAVE_SP_EEPROM_HEADER_BIG = 5U, | ||
| 37 | }; | ||
| 38 | |||
| 39 | #define RAVE_SP_EEPROM_PAGE_SIZE 32U | ||
| 40 | |||
| 41 | /** | ||
| 42 | * struct rave_sp_eeprom_page - RAVE SP EEPROM page | ||
| 43 | * | ||
| 44 | * @type: Access type (see enum rave_sp_eeprom_access_type) | ||
| 45 | * @success: Success flag (Success = 1, Failure = 0) | ||
| 46 | * @data: Read data | ||
| 47 | |||
| 48 | * Note this structure corresponds to RSP_*_EEPROM payload from RAVE | ||
| 49 | * SP ICD | ||
| 50 | */ | ||
| 51 | struct rave_sp_eeprom_page { | ||
| 52 | u8 type; | ||
| 53 | u8 success; | ||
| 54 | u8 data[RAVE_SP_EEPROM_PAGE_SIZE]; | ||
| 55 | } __packed; | ||
| 56 | |||
| 57 | /** | ||
| 58 | * struct rave_sp_eeprom - RAVE SP EEPROM device | ||
| 59 | * | ||
| 60 | * @sp: Pointer to parent RAVE SP device | ||
| 61 | * @mutex: Lock protecting access to EEPROM | ||
| 62 | * @address: EEPROM device address | ||
| 63 | * @header_size: Size of EEPROM command header for this device | ||
| 64 | * @dev: Pointer to corresponding struct device used for logging | ||
| 65 | */ | ||
| 66 | struct rave_sp_eeprom { | ||
| 67 | struct rave_sp *sp; | ||
| 68 | struct mutex mutex; | ||
| 69 | u8 address; | ||
| 70 | unsigned int header_size; | ||
| 71 | struct device *dev; | ||
| 72 | }; | ||
| 73 | |||
| 74 | /** | ||
| 75 | * rave_sp_eeprom_io - Low-level part of EEPROM page access | ||
| 76 | * | ||
| 77 | * @eeprom: EEPROM device to write to | ||
| 78 | * @type: EEPROM access type (read or write) | ||
| 79 | * @idx: number of the EEPROM page | ||
| 80 | * @page: Data to write or buffer to store result (via page->data) | ||
| 81 | * | ||
| 82 | * This function does all of the low-level work required to perform a | ||
| 83 | * EEPROM access. This includes formatting correct command payload, | ||
| 84 | * sending it and checking received results. | ||
| 85 | * | ||
| 86 | * Returns zero in case of success or negative error code in | ||
| 87 | * case of failure. | ||
| 88 | */ | ||
| 89 | static int rave_sp_eeprom_io(struct rave_sp_eeprom *eeprom, | ||
| 90 | enum rave_sp_eeprom_access_type type, | ||
| 91 | u16 idx, | ||
| 92 | struct rave_sp_eeprom_page *page) | ||
| 93 | { | ||
| 94 | const bool is_write = type == RAVE_SP_EEPROM_WRITE; | ||
| 95 | const unsigned int data_size = is_write ? sizeof(page->data) : 0; | ||
| 96 | const unsigned int cmd_size = eeprom->header_size + data_size; | ||
| 97 | const unsigned int rsp_size = | ||
| 98 | is_write ? sizeof(*page) - sizeof(page->data) : sizeof(*page); | ||
| 99 | unsigned int offset = 0; | ||
| 100 | u8 cmd[cmd_size]; | ||
| 101 | int ret; | ||
| 102 | |||
| 103 | cmd[offset++] = eeprom->address; | ||
| 104 | cmd[offset++] = 0; | ||
| 105 | cmd[offset++] = type; | ||
| 106 | cmd[offset++] = idx; | ||
| 107 | |||
| 108 | /* | ||
| 109 | * If there's still room in this command's header it means we | ||
| 110 | * are talkin to EEPROM that uses 16-bit page numbers and we | ||
| 111 | * have to specify index's MSB in payload as well. | ||
| 112 | */ | ||
| 113 | if (offset < eeprom->header_size) | ||
| 114 | cmd[offset++] = idx >> 8; | ||
| 115 | /* | ||
| 116 | * Copy our data to write to command buffer first. In case of | ||
| 117 | * a read data_size should be zero and memcpy would become a | ||
| 118 | * no-op | ||
| 119 | */ | ||
| 120 | memcpy(&cmd[offset], page->data, data_size); | ||
| 121 | |||
| 122 | ret = rave_sp_exec(eeprom->sp, cmd, cmd_size, page, rsp_size); | ||
| 123 | if (ret) | ||
| 124 | return ret; | ||
| 125 | |||
| 126 | if (page->type != type) | ||
| 127 | return -EPROTO; | ||
| 128 | |||
| 129 | if (!page->success) | ||
| 130 | return -EIO; | ||
| 131 | |||
| 132 | return 0; | ||
| 133 | } | ||
| 134 | |||
| 135 | /** | ||
| 136 | * rave_sp_eeprom_page_access - Access single EEPROM page | ||
| 137 | * | ||
| 138 | * @eeprom: EEPROM device to access | ||
| 139 | * @type: Access type to perform (read or write) | ||
| 140 | * @offset: Offset within EEPROM to access | ||
| 141 | * @data: Data buffer | ||
| 142 | * @data_len: Size of the data buffer | ||
| 143 | * | ||
| 144 | * This function performs a generic access to a single page or a | ||
| 145 | * portion thereof. Requested access MUST NOT cross the EEPROM page | ||
| 146 | * boundary. | ||
| 147 | * | ||
| 148 | * Returns zero in case of success or negative error code in | ||
| 149 | * case of failure. | ||
| 150 | */ | ||
| 151 | static int | ||
| 152 | rave_sp_eeprom_page_access(struct rave_sp_eeprom *eeprom, | ||
| 153 | enum rave_sp_eeprom_access_type type, | ||
| 154 | unsigned int offset, u8 *data, | ||
| 155 | size_t data_len) | ||
| 156 | { | ||
| 157 | const unsigned int page_offset = offset % RAVE_SP_EEPROM_PAGE_SIZE; | ||
| 158 | const unsigned int page_nr = offset / RAVE_SP_EEPROM_PAGE_SIZE; | ||
| 159 | struct rave_sp_eeprom_page page; | ||
| 160 | int ret; | ||
| 161 | |||
| 162 | /* | ||
| 163 | * This function will not work if data access we've been asked | ||
| 164 | * to do is crossing EEPROM page boundary. Normally this | ||
| 165 | * should never happen and getting here would indicate a bug | ||
| 166 | * in the code. | ||
| 167 | */ | ||
| 168 | if (WARN_ON(data_len > sizeof(page.data) - page_offset)) | ||
| 169 | return -EINVAL; | ||
| 170 | |||
| 171 | if (type == RAVE_SP_EEPROM_WRITE) { | ||
| 172 | /* | ||
| 173 | * If doing a partial write we need to do a read first | ||
| 174 | * to fill the rest of the page with correct data. | ||
| 175 | */ | ||
| 176 | if (data_len < RAVE_SP_EEPROM_PAGE_SIZE) { | ||
| 177 | ret = rave_sp_eeprom_io(eeprom, RAVE_SP_EEPROM_READ, | ||
| 178 | page_nr, &page); | ||
| 179 | if (ret) | ||
| 180 | return ret; | ||
| 181 | } | ||
| 182 | |||
| 183 | memcpy(&page.data[page_offset], data, data_len); | ||
| 184 | } | ||
| 185 | |||
| 186 | ret = rave_sp_eeprom_io(eeprom, type, page_nr, &page); | ||
| 187 | if (ret) | ||
| 188 | return ret; | ||
| 189 | |||
| 190 | /* | ||
| 191 | * Since we receive the result of the read via 'page.data' | ||
| 192 | * buffer we need to copy that to 'data' | ||
| 193 | */ | ||
| 194 | if (type == RAVE_SP_EEPROM_READ) | ||
| 195 | memcpy(data, &page.data[page_offset], data_len); | ||
| 196 | |||
| 197 | return 0; | ||
| 198 | } | ||
| 199 | |||
| 200 | /** | ||
| 201 | * rave_sp_eeprom_access - Access EEPROM data | ||
| 202 | * | ||
| 203 | * @eeprom: EEPROM device to access | ||
| 204 | * @type: Access type to perform (read or write) | ||
| 205 | * @offset: Offset within EEPROM to access | ||
| 206 | * @data: Data buffer | ||
| 207 | * @data_len: Size of the data buffer | ||
| 208 | * | ||
| 209 | * This function performs a generic access (either read or write) at | ||
| 210 | * arbitrary offset (not necessary page aligned) of arbitrary length | ||
| 211 | * (is not constrained by EEPROM page size). | ||
| 212 | * | ||
| 213 | * Returns zero in case of success or negative error code in case of | ||
| 214 | * failure. | ||
| 215 | */ | ||
| 216 | static int rave_sp_eeprom_access(struct rave_sp_eeprom *eeprom, | ||
| 217 | enum rave_sp_eeprom_access_type type, | ||
| 218 | unsigned int offset, u8 *data, | ||
| 219 | unsigned int data_len) | ||
| 220 | { | ||
| 221 | unsigned int residue; | ||
| 222 | unsigned int chunk; | ||
| 223 | unsigned int head; | ||
| 224 | int ret; | ||
| 225 | |||
| 226 | mutex_lock(&eeprom->mutex); | ||
| 227 | |||
| 228 | head = offset % RAVE_SP_EEPROM_PAGE_SIZE; | ||
| 229 | residue = data_len; | ||
| 230 | |||
| 231 | do { | ||
| 232 | /* | ||
| 233 | * First iteration, if we are doing an access that is | ||
| 234 | * not 32-byte aligned, we need to access only data up | ||
| 235 | * to a page boundary to avoid corssing it in | ||
| 236 | * rave_sp_eeprom_page_access() | ||
| 237 | */ | ||
| 238 | if (unlikely(head)) { | ||
| 239 | chunk = RAVE_SP_EEPROM_PAGE_SIZE - head; | ||
| 240 | /* | ||
| 241 | * This can only happen once per | ||
| 242 | * rave_sp_eeprom_access() call, so we set | ||
| 243 | * head to zero to process all the other | ||
| 244 | * iterations normally. | ||
| 245 | */ | ||
| 246 | head = 0; | ||
| 247 | } else { | ||
| 248 | chunk = RAVE_SP_EEPROM_PAGE_SIZE; | ||
| 249 | } | ||
| 250 | |||
| 251 | /* | ||
| 252 | * We should never read more that 'residue' bytes | ||
| 253 | */ | ||
| 254 | chunk = min(chunk, residue); | ||
| 255 | ret = rave_sp_eeprom_page_access(eeprom, type, offset, | ||
| 256 | data, chunk); | ||
| 257 | if (ret) | ||
| 258 | goto out; | ||
| 259 | |||
| 260 | residue -= chunk; | ||
| 261 | offset += chunk; | ||
| 262 | data += chunk; | ||
| 263 | } while (residue); | ||
| 264 | out: | ||
| 265 | mutex_unlock(&eeprom->mutex); | ||
| 266 | return ret; | ||
| 267 | } | ||
| 268 | |||
| 269 | static int rave_sp_eeprom_reg_read(void *eeprom, unsigned int offset, | ||
| 270 | void *val, size_t bytes) | ||
| 271 | { | ||
| 272 | return rave_sp_eeprom_access(eeprom, RAVE_SP_EEPROM_READ, | ||
| 273 | offset, val, bytes); | ||
| 274 | } | ||
| 275 | |||
| 276 | static int rave_sp_eeprom_reg_write(void *eeprom, unsigned int offset, | ||
| 277 | void *val, size_t bytes) | ||
| 278 | { | ||
| 279 | return rave_sp_eeprom_access(eeprom, RAVE_SP_EEPROM_WRITE, | ||
| 280 | offset, val, bytes); | ||
| 281 | } | ||
| 282 | |||
| 283 | static int rave_sp_eeprom_probe(struct platform_device *pdev) | ||
| 284 | { | ||
| 285 | struct device *dev = &pdev->dev; | ||
| 286 | struct rave_sp *sp = dev_get_drvdata(dev->parent); | ||
| 287 | struct device_node *np = dev->of_node; | ||
| 288 | struct nvmem_config config = { 0 }; | ||
| 289 | struct rave_sp_eeprom *eeprom; | ||
| 290 | struct nvmem_device *nvmem; | ||
| 291 | u32 reg[2], size; | ||
| 292 | |||
| 293 | if (of_property_read_u32_array(np, "reg", reg, ARRAY_SIZE(reg))) { | ||
| 294 | dev_err(dev, "Failed to parse \"reg\" property\n"); | ||
| 295 | return -EINVAL; | ||
| 296 | } | ||
| 297 | |||
| 298 | size = reg[1]; | ||
| 299 | /* | ||
| 300 | * Per ICD, we have no more than 2 bytes to specify EEPROM | ||
| 301 | * page. | ||
| 302 | */ | ||
| 303 | if (size > U16_MAX * RAVE_SP_EEPROM_PAGE_SIZE) { | ||
| 304 | dev_err(dev, "Specified size is too big\n"); | ||
| 305 | return -EINVAL; | ||
| 306 | } | ||
| 307 | |||
| 308 | eeprom = devm_kzalloc(dev, sizeof(*eeprom), GFP_KERNEL); | ||
| 309 | if (!eeprom) | ||
| 310 | return -ENOMEM; | ||
| 311 | |||
| 312 | eeprom->address = reg[0]; | ||
| 313 | eeprom->sp = sp; | ||
| 314 | eeprom->dev = dev; | ||
| 315 | |||
| 316 | if (size > SZ_8K) | ||
| 317 | eeprom->header_size = RAVE_SP_EEPROM_HEADER_BIG; | ||
| 318 | else | ||
| 319 | eeprom->header_size = RAVE_SP_EEPROM_HEADER_SMALL; | ||
| 320 | |||
| 321 | mutex_init(&eeprom->mutex); | ||
| 322 | |||
| 323 | config.id = -1; | ||
| 324 | of_property_read_string(np, "zii,eeprom-name", &config.name); | ||
| 325 | config.priv = eeprom; | ||
| 326 | config.dev = dev; | ||
| 327 | config.size = size; | ||
| 328 | config.reg_read = rave_sp_eeprom_reg_read; | ||
| 329 | config.reg_write = rave_sp_eeprom_reg_write; | ||
| 330 | config.word_size = 1; | ||
| 331 | config.stride = 1; | ||
| 332 | |||
| 333 | nvmem = devm_nvmem_register(dev, &config); | ||
| 334 | |||
| 335 | return PTR_ERR_OR_ZERO(nvmem); | ||
| 336 | } | ||
| 337 | |||
| 338 | static const struct of_device_id rave_sp_eeprom_of_match[] = { | ||
| 339 | { .compatible = "zii,rave-sp-eeprom" }, | ||
| 340 | {} | ||
| 341 | }; | ||
| 342 | MODULE_DEVICE_TABLE(of, rave_sp_eeprom_of_match); | ||
| 343 | |||
| 344 | static struct platform_driver rave_sp_eeprom_driver = { | ||
| 345 | .probe = rave_sp_eeprom_probe, | ||
| 346 | .driver = { | ||
| 347 | .name = KBUILD_MODNAME, | ||
| 348 | .of_match_table = rave_sp_eeprom_of_match, | ||
| 349 | }, | ||
| 350 | }; | ||
| 351 | module_platform_driver(rave_sp_eeprom_driver); | ||
| 352 | |||
| 353 | MODULE_LICENSE("GPL"); | ||
| 354 | MODULE_AUTHOR("Andrey Vostrikov <andrey.vostrikov@cogentembedded.com>"); | ||
| 355 | MODULE_AUTHOR("Nikita Yushchenko <nikita.yoush@cogentembedded.com>"); | ||
| 356 | MODULE_AUTHOR("Andrey Smirnov <andrew.smirnov@gmail.com>"); | ||
| 357 | MODULE_DESCRIPTION("RAVE SP EEPROM driver"); | ||
diff --git a/drivers/slimbus/qcom-ctrl.c b/drivers/slimbus/qcom-ctrl.c index ffb46f915334..bb36a8fbc9b1 100644 --- a/drivers/slimbus/qcom-ctrl.c +++ b/drivers/slimbus/qcom-ctrl.c | |||
| @@ -439,13 +439,12 @@ static int slim_get_current_rxbuf(struct qcom_slim_ctrl *ctrl, void *buf) | |||
| 439 | static void qcom_slim_rxwq(struct work_struct *work) | 439 | static void qcom_slim_rxwq(struct work_struct *work) |
| 440 | { | 440 | { |
| 441 | u8 buf[SLIM_MSGQ_BUF_LEN]; | 441 | u8 buf[SLIM_MSGQ_BUF_LEN]; |
| 442 | u8 mc, mt, len; | 442 | u8 mc, mt; |
| 443 | int ret; | 443 | int ret; |
| 444 | struct qcom_slim_ctrl *ctrl = container_of(work, struct qcom_slim_ctrl, | 444 | struct qcom_slim_ctrl *ctrl = container_of(work, struct qcom_slim_ctrl, |
| 445 | wd); | 445 | wd); |
| 446 | 446 | ||
| 447 | while ((slim_get_current_rxbuf(ctrl, buf)) != -ENODATA) { | 447 | while ((slim_get_current_rxbuf(ctrl, buf)) != -ENODATA) { |
| 448 | len = SLIM_HEADER_GET_RL(buf[0]); | ||
| 449 | mt = SLIM_HEADER_GET_MT(buf[0]); | 448 | mt = SLIM_HEADER_GET_MT(buf[0]); |
| 450 | mc = SLIM_HEADER_GET_MC(buf[1]); | 449 | mc = SLIM_HEADER_GET_MC(buf[1]); |
| 451 | if (mt == SLIM_MSG_MT_CORE && | 450 | if (mt == SLIM_MSG_MT_CORE && |
diff --git a/drivers/soundwire/Kconfig b/drivers/soundwire/Kconfig index b46084b4b1f8..19c8efb9a5ee 100644 --- a/drivers/soundwire/Kconfig +++ b/drivers/soundwire/Kconfig | |||
| @@ -27,7 +27,7 @@ config SOUNDWIRE_INTEL | |||
| 27 | tristate "Intel SoundWire Master driver" | 27 | tristate "Intel SoundWire Master driver" |
| 28 | select SOUNDWIRE_CADENCE | 28 | select SOUNDWIRE_CADENCE |
| 29 | select SOUNDWIRE_BUS | 29 | select SOUNDWIRE_BUS |
| 30 | depends on X86 && ACPI | 30 | depends on X86 && ACPI && SND_SOC |
| 31 | ---help--- | 31 | ---help--- |
| 32 | SoundWire Intel Master driver. | 32 | SoundWire Intel Master driver. |
| 33 | If you have an Intel platform which has a SoundWire Master then | 33 | If you have an Intel platform which has a SoundWire Master then |
diff --git a/drivers/soundwire/Makefile b/drivers/soundwire/Makefile index e1a74c5692aa..5817beaca0e1 100644 --- a/drivers/soundwire/Makefile +++ b/drivers/soundwire/Makefile | |||
| @@ -3,7 +3,7 @@ | |||
| 3 | # | 3 | # |
| 4 | 4 | ||
| 5 | #Bus Objs | 5 | #Bus Objs |
| 6 | soundwire-bus-objs := bus_type.o bus.o slave.o mipi_disco.o | 6 | soundwire-bus-objs := bus_type.o bus.o slave.o mipi_disco.o stream.o |
| 7 | obj-$(CONFIG_SOUNDWIRE_BUS) += soundwire-bus.o | 7 | obj-$(CONFIG_SOUNDWIRE_BUS) += soundwire-bus.o |
| 8 | 8 | ||
| 9 | #Cadence Objs | 9 | #Cadence Objs |
diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index d6dc8e7a8614..dcc0ff9f0c22 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c | |||
| @@ -17,6 +17,7 @@ | |||
| 17 | */ | 17 | */ |
| 18 | int sdw_add_bus_master(struct sdw_bus *bus) | 18 | int sdw_add_bus_master(struct sdw_bus *bus) |
| 19 | { | 19 | { |
| 20 | struct sdw_master_prop *prop = NULL; | ||
| 20 | int ret; | 21 | int ret; |
| 21 | 22 | ||
| 22 | if (!bus->dev) { | 23 | if (!bus->dev) { |
| @@ -32,6 +33,7 @@ int sdw_add_bus_master(struct sdw_bus *bus) | |||
| 32 | mutex_init(&bus->msg_lock); | 33 | mutex_init(&bus->msg_lock); |
| 33 | mutex_init(&bus->bus_lock); | 34 | mutex_init(&bus->bus_lock); |
| 34 | INIT_LIST_HEAD(&bus->slaves); | 35 | INIT_LIST_HEAD(&bus->slaves); |
| 36 | INIT_LIST_HEAD(&bus->m_rt_list); | ||
| 35 | 37 | ||
| 36 | if (bus->ops->read_prop) { | 38 | if (bus->ops->read_prop) { |
| 37 | ret = bus->ops->read_prop(bus); | 39 | ret = bus->ops->read_prop(bus); |
| @@ -77,6 +79,21 @@ int sdw_add_bus_master(struct sdw_bus *bus) | |||
| 77 | return ret; | 79 | return ret; |
| 78 | } | 80 | } |
| 79 | 81 | ||
| 82 | /* | ||
| 83 | * Initialize clock values based on Master properties. The max | ||
| 84 | * frequency is read from max_freq property. Current assumption | ||
| 85 | * is that the bus will start at highest clock frequency when | ||
| 86 | * powered on. | ||
| 87 | * | ||
| 88 | * Default active bank will be 0 as out of reset the Slaves have | ||
| 89 | * to start with bank 0 (Table 40 of Spec) | ||
| 90 | */ | ||
| 91 | prop = &bus->prop; | ||
| 92 | bus->params.max_dr_freq = prop->max_freq * SDW_DOUBLE_RATE_FACTOR; | ||
| 93 | bus->params.curr_dr_freq = bus->params.max_dr_freq; | ||
| 94 | bus->params.curr_bank = SDW_BANK0; | ||
| 95 | bus->params.next_bank = SDW_BANK1; | ||
| 96 | |||
| 80 | return 0; | 97 | return 0; |
| 81 | } | 98 | } |
| 82 | EXPORT_SYMBOL(sdw_add_bus_master); | 99 | EXPORT_SYMBOL(sdw_add_bus_master); |
| @@ -576,6 +593,32 @@ static void sdw_modify_slave_status(struct sdw_slave *slave, | |||
| 576 | mutex_unlock(&slave->bus->bus_lock); | 593 | mutex_unlock(&slave->bus->bus_lock); |
| 577 | } | 594 | } |
| 578 | 595 | ||
| 596 | int sdw_configure_dpn_intr(struct sdw_slave *slave, | ||
| 597 | int port, bool enable, int mask) | ||
| 598 | { | ||
| 599 | u32 addr; | ||
| 600 | int ret; | ||
| 601 | u8 val = 0; | ||
| 602 | |||
| 603 | addr = SDW_DPN_INTMASK(port); | ||
| 604 | |||
| 605 | /* Set/Clear port ready interrupt mask */ | ||
| 606 | if (enable) { | ||
| 607 | val |= mask; | ||
| 608 | val |= SDW_DPN_INT_PORT_READY; | ||
| 609 | } else { | ||
| 610 | val &= ~(mask); | ||
| 611 | val &= ~SDW_DPN_INT_PORT_READY; | ||
| 612 | } | ||
| 613 | |||
| 614 | ret = sdw_update(slave, addr, (mask | SDW_DPN_INT_PORT_READY), val); | ||
| 615 | if (ret < 0) | ||
| 616 | dev_err(slave->bus->dev, | ||
| 617 | "SDW_DPN_INTMASK write failed:%d", val); | ||
| 618 | |||
| 619 | return ret; | ||
| 620 | } | ||
| 621 | |||
| 579 | static int sdw_initialize_slave(struct sdw_slave *slave) | 622 | static int sdw_initialize_slave(struct sdw_slave *slave) |
| 580 | { | 623 | { |
| 581 | struct sdw_slave_prop *prop = &slave->prop; | 624 | struct sdw_slave_prop *prop = &slave->prop; |
diff --git a/drivers/soundwire/bus.h b/drivers/soundwire/bus.h index 345c34d697e9..3b15c4e25a3a 100644 --- a/drivers/soundwire/bus.h +++ b/drivers/soundwire/bus.h | |||
| @@ -45,6 +45,78 @@ struct sdw_msg { | |||
| 45 | bool page; | 45 | bool page; |
| 46 | }; | 46 | }; |
| 47 | 47 | ||
| 48 | #define SDW_DOUBLE_RATE_FACTOR 2 | ||
| 49 | |||
| 50 | extern int rows[SDW_FRAME_ROWS]; | ||
| 51 | extern int cols[SDW_FRAME_COLS]; | ||
| 52 | |||
| 53 | /** | ||
| 54 | * sdw_port_runtime: Runtime port parameters for Master or Slave | ||
| 55 | * | ||
| 56 | * @num: Port number. For audio streams, valid port number ranges from | ||
| 57 | * [1,14] | ||
| 58 | * @ch_mask: Channel mask | ||
| 59 | * @transport_params: Transport parameters | ||
| 60 | * @port_params: Port parameters | ||
| 61 | * @port_node: List node for Master or Slave port_list | ||
| 62 | * | ||
| 63 | * SoundWire spec has no mention of ports for Master interface but the | ||
| 64 | * concept is logically extended. | ||
| 65 | */ | ||
| 66 | struct sdw_port_runtime { | ||
| 67 | int num; | ||
| 68 | int ch_mask; | ||
| 69 | struct sdw_transport_params transport_params; | ||
| 70 | struct sdw_port_params port_params; | ||
| 71 | struct list_head port_node; | ||
| 72 | }; | ||
| 73 | |||
| 74 | /** | ||
| 75 | * sdw_slave_runtime: Runtime Stream parameters for Slave | ||
| 76 | * | ||
| 77 | * @slave: Slave handle | ||
| 78 | * @direction: Data direction for Slave | ||
| 79 | * @ch_count: Number of channels handled by the Slave for | ||
| 80 | * this stream | ||
| 81 | * @m_rt_node: sdw_master_runtime list node | ||
| 82 | * @port_list: List of Slave Ports configured for this stream | ||
| 83 | */ | ||
| 84 | struct sdw_slave_runtime { | ||
| 85 | struct sdw_slave *slave; | ||
| 86 | enum sdw_data_direction direction; | ||
| 87 | unsigned int ch_count; | ||
| 88 | struct list_head m_rt_node; | ||
| 89 | struct list_head port_list; | ||
| 90 | }; | ||
| 91 | |||
| 92 | /** | ||
| 93 | * sdw_master_runtime: Runtime stream parameters for Master | ||
| 94 | * | ||
| 95 | * @bus: Bus handle | ||
| 96 | * @stream: Stream runtime handle | ||
| 97 | * @direction: Data direction for Master | ||
| 98 | * @ch_count: Number of channels handled by the Master for | ||
| 99 | * this stream, can be zero. | ||
| 100 | * @slave_rt_list: Slave runtime list | ||
| 101 | * @port_list: List of Master Ports configured for this stream, can be zero. | ||
| 102 | * @bus_node: sdw_bus m_rt_list node | ||
| 103 | */ | ||
| 104 | struct sdw_master_runtime { | ||
| 105 | struct sdw_bus *bus; | ||
| 106 | struct sdw_stream_runtime *stream; | ||
| 107 | enum sdw_data_direction direction; | ||
| 108 | unsigned int ch_count; | ||
| 109 | struct list_head slave_rt_list; | ||
| 110 | struct list_head port_list; | ||
| 111 | struct list_head bus_node; | ||
| 112 | }; | ||
| 113 | |||
| 114 | struct sdw_dpn_prop *sdw_get_slave_dpn_prop(struct sdw_slave *slave, | ||
| 115 | enum sdw_data_direction direction, | ||
| 116 | unsigned int port_num); | ||
| 117 | int sdw_configure_dpn_intr(struct sdw_slave *slave, int port, | ||
| 118 | bool enable, int mask); | ||
| 119 | |||
| 48 | int sdw_transfer(struct sdw_bus *bus, struct sdw_msg *msg); | 120 | int sdw_transfer(struct sdw_bus *bus, struct sdw_msg *msg); |
| 49 | int sdw_transfer_defer(struct sdw_bus *bus, struct sdw_msg *msg, | 121 | int sdw_transfer_defer(struct sdw_bus *bus, struct sdw_msg *msg, |
| 50 | struct sdw_defer *defer); | 122 | struct sdw_defer *defer); |
diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index 3a9b1462039b..cb6a331f448a 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c | |||
| @@ -13,6 +13,8 @@ | |||
| 13 | #include <linux/mod_devicetable.h> | 13 | #include <linux/mod_devicetable.h> |
| 14 | #include <linux/soundwire/sdw_registers.h> | 14 | #include <linux/soundwire/sdw_registers.h> |
| 15 | #include <linux/soundwire/sdw.h> | 15 | #include <linux/soundwire/sdw.h> |
| 16 | #include <sound/pcm_params.h> | ||
| 17 | #include <sound/soc.h> | ||
| 16 | #include "bus.h" | 18 | #include "bus.h" |
| 17 | #include "cadence_master.h" | 19 | #include "cadence_master.h" |
| 18 | 20 | ||
| @@ -396,7 +398,7 @@ static int cdns_prep_msg(struct sdw_cdns *cdns, struct sdw_msg *msg, int *cmd) | |||
| 396 | return 0; | 398 | return 0; |
| 397 | } | 399 | } |
| 398 | 400 | ||
| 399 | static enum sdw_command_response | 401 | enum sdw_command_response |
| 400 | cdns_xfer_msg(struct sdw_bus *bus, struct sdw_msg *msg) | 402 | cdns_xfer_msg(struct sdw_bus *bus, struct sdw_msg *msg) |
| 401 | { | 403 | { |
| 402 | struct sdw_cdns *cdns = bus_to_cdns(bus); | 404 | struct sdw_cdns *cdns = bus_to_cdns(bus); |
| @@ -422,8 +424,9 @@ cdns_xfer_msg(struct sdw_bus *bus, struct sdw_msg *msg) | |||
| 422 | exit: | 424 | exit: |
| 423 | return ret; | 425 | return ret; |
| 424 | } | 426 | } |
| 427 | EXPORT_SYMBOL(cdns_xfer_msg); | ||
| 425 | 428 | ||
| 426 | static enum sdw_command_response | 429 | enum sdw_command_response |
| 427 | cdns_xfer_msg_defer(struct sdw_bus *bus, | 430 | cdns_xfer_msg_defer(struct sdw_bus *bus, |
| 428 | struct sdw_msg *msg, struct sdw_defer *defer) | 431 | struct sdw_msg *msg, struct sdw_defer *defer) |
| 429 | { | 432 | { |
| @@ -443,8 +446,9 @@ cdns_xfer_msg_defer(struct sdw_bus *bus, | |||
| 443 | 446 | ||
| 444 | return _cdns_xfer_msg(cdns, msg, cmd, 0, msg->len, true); | 447 | return _cdns_xfer_msg(cdns, msg, cmd, 0, msg->len, true); |
| 445 | } | 448 | } |
| 449 | EXPORT_SYMBOL(cdns_xfer_msg_defer); | ||
| 446 | 450 | ||
| 447 | static enum sdw_command_response | 451 | enum sdw_command_response |
| 448 | cdns_reset_page_addr(struct sdw_bus *bus, unsigned int dev_num) | 452 | cdns_reset_page_addr(struct sdw_bus *bus, unsigned int dev_num) |
| 449 | { | 453 | { |
| 450 | struct sdw_cdns *cdns = bus_to_cdns(bus); | 454 | struct sdw_cdns *cdns = bus_to_cdns(bus); |
| @@ -456,6 +460,7 @@ cdns_reset_page_addr(struct sdw_bus *bus, unsigned int dev_num) | |||
| 456 | 460 | ||
| 457 | return cdns_program_scp_addr(cdns, &msg); | 461 | return cdns_program_scp_addr(cdns, &msg); |
| 458 | } | 462 | } |
| 463 | EXPORT_SYMBOL(cdns_reset_page_addr); | ||
| 459 | 464 | ||
| 460 | /* | 465 | /* |
| 461 | * IRQ handling | 466 | * IRQ handling |
| @@ -666,6 +671,120 @@ int sdw_cdns_enable_interrupt(struct sdw_cdns *cdns) | |||
| 666 | } | 671 | } |
| 667 | EXPORT_SYMBOL(sdw_cdns_enable_interrupt); | 672 | EXPORT_SYMBOL(sdw_cdns_enable_interrupt); |
| 668 | 673 | ||
| 674 | static int cdns_allocate_pdi(struct sdw_cdns *cdns, | ||
| 675 | struct sdw_cdns_pdi **stream, | ||
| 676 | u32 num, u32 pdi_offset) | ||
| 677 | { | ||
| 678 | struct sdw_cdns_pdi *pdi; | ||
| 679 | int i; | ||
| 680 | |||
| 681 | if (!num) | ||
| 682 | return 0; | ||
| 683 | |||
| 684 | pdi = devm_kcalloc(cdns->dev, num, sizeof(*pdi), GFP_KERNEL); | ||
| 685 | if (!pdi) | ||
| 686 | return -ENOMEM; | ||
| 687 | |||
| 688 | for (i = 0; i < num; i++) { | ||
| 689 | pdi[i].num = i + pdi_offset; | ||
| 690 | pdi[i].assigned = false; | ||
| 691 | } | ||
| 692 | |||
| 693 | *stream = pdi; | ||
| 694 | return 0; | ||
| 695 | } | ||
| 696 | |||
| 697 | /** | ||
| 698 | * sdw_cdns_pdi_init() - PDI initialization routine | ||
| 699 | * | ||
| 700 | * @cdns: Cadence instance | ||
| 701 | * @config: Stream configurations | ||
| 702 | */ | ||
| 703 | int sdw_cdns_pdi_init(struct sdw_cdns *cdns, | ||
| 704 | struct sdw_cdns_stream_config config) | ||
| 705 | { | ||
| 706 | struct sdw_cdns_streams *stream; | ||
| 707 | int offset, i, ret; | ||
| 708 | |||
| 709 | cdns->pcm.num_bd = config.pcm_bd; | ||
| 710 | cdns->pcm.num_in = config.pcm_in; | ||
| 711 | cdns->pcm.num_out = config.pcm_out; | ||
| 712 | cdns->pdm.num_bd = config.pdm_bd; | ||
| 713 | cdns->pdm.num_in = config.pdm_in; | ||
| 714 | cdns->pdm.num_out = config.pdm_out; | ||
| 715 | |||
| 716 | /* Allocate PDIs for PCMs */ | ||
| 717 | stream = &cdns->pcm; | ||
| 718 | |||
| 719 | /* First two PDIs are reserved for bulk transfers */ | ||
| 720 | stream->num_bd -= CDNS_PCM_PDI_OFFSET; | ||
| 721 | offset = CDNS_PCM_PDI_OFFSET; | ||
| 722 | |||
| 723 | ret = cdns_allocate_pdi(cdns, &stream->bd, | ||
| 724 | stream->num_bd, offset); | ||
| 725 | if (ret) | ||
| 726 | return ret; | ||
| 727 | |||
| 728 | offset += stream->num_bd; | ||
| 729 | |||
| 730 | ret = cdns_allocate_pdi(cdns, &stream->in, | ||
| 731 | stream->num_in, offset); | ||
| 732 | if (ret) | ||
| 733 | return ret; | ||
| 734 | |||
| 735 | offset += stream->num_in; | ||
| 736 | |||
| 737 | ret = cdns_allocate_pdi(cdns, &stream->out, | ||
| 738 | stream->num_out, offset); | ||
| 739 | if (ret) | ||
| 740 | return ret; | ||
| 741 | |||
| 742 | /* Update total number of PCM PDIs */ | ||
| 743 | stream->num_pdi = stream->num_bd + stream->num_in + stream->num_out; | ||
| 744 | cdns->num_ports = stream->num_pdi; | ||
| 745 | |||
| 746 | /* Allocate PDIs for PDMs */ | ||
| 747 | stream = &cdns->pdm; | ||
| 748 | offset = CDNS_PDM_PDI_OFFSET; | ||
| 749 | ret = cdns_allocate_pdi(cdns, &stream->bd, | ||
| 750 | stream->num_bd, offset); | ||
| 751 | if (ret) | ||
| 752 | return ret; | ||
| 753 | |||
| 754 | offset += stream->num_bd; | ||
| 755 | |||
| 756 | ret = cdns_allocate_pdi(cdns, &stream->in, | ||
| 757 | stream->num_in, offset); | ||
| 758 | if (ret) | ||
| 759 | return ret; | ||
| 760 | |||
| 761 | offset += stream->num_in; | ||
| 762 | |||
| 763 | ret = cdns_allocate_pdi(cdns, &stream->out, | ||
| 764 | stream->num_out, offset); | ||
| 765 | if (ret) | ||
| 766 | return ret; | ||
| 767 | |||
| 768 | /* Update total number of PDM PDIs */ | ||
| 769 | stream->num_pdi = stream->num_bd + stream->num_in + stream->num_out; | ||
| 770 | cdns->num_ports += stream->num_pdi; | ||
| 771 | |||
| 772 | cdns->ports = devm_kcalloc(cdns->dev, cdns->num_ports, | ||
| 773 | sizeof(*cdns->ports), GFP_KERNEL); | ||
| 774 | if (!cdns->ports) { | ||
| 775 | ret = -ENOMEM; | ||
| 776 | return ret; | ||
| 777 | } | ||
| 778 | |||
| 779 | for (i = 0; i < cdns->num_ports; i++) { | ||
| 780 | cdns->ports[i].assigned = false; | ||
| 781 | cdns->ports[i].num = i + 1; /* Port 0 reserved for bulk */ | ||
| 782 | } | ||
| 783 | |||
| 784 | return 0; | ||
| 785 | } | ||
| 786 | EXPORT_SYMBOL(sdw_cdns_pdi_init); | ||
| 787 | |||
| 669 | /** | 788 | /** |
| 670 | * sdw_cdns_init() - Cadence initialization | 789 | * sdw_cdns_init() - Cadence initialization |
| 671 | * @cdns: Cadence instance | 790 | * @cdns: Cadence instance |
| @@ -727,13 +846,133 @@ int sdw_cdns_init(struct sdw_cdns *cdns) | |||
| 727 | } | 846 | } |
| 728 | EXPORT_SYMBOL(sdw_cdns_init); | 847 | EXPORT_SYMBOL(sdw_cdns_init); |
| 729 | 848 | ||
| 730 | struct sdw_master_ops sdw_cdns_master_ops = { | 849 | int cdns_bus_conf(struct sdw_bus *bus, struct sdw_bus_params *params) |
| 731 | .read_prop = sdw_master_read_prop, | 850 | { |
| 732 | .xfer_msg = cdns_xfer_msg, | 851 | struct sdw_cdns *cdns = bus_to_cdns(bus); |
| 733 | .xfer_msg_defer = cdns_xfer_msg_defer, | 852 | int mcp_clkctrl_off, mcp_clkctrl; |
| 734 | .reset_page_addr = cdns_reset_page_addr, | 853 | int divider; |
| 854 | |||
| 855 | if (!params->curr_dr_freq) { | ||
| 856 | dev_err(cdns->dev, "NULL curr_dr_freq"); | ||
| 857 | return -EINVAL; | ||
| 858 | } | ||
| 859 | |||
| 860 | divider = (params->max_dr_freq / params->curr_dr_freq) - 1; | ||
| 861 | |||
| 862 | if (params->next_bank) | ||
| 863 | mcp_clkctrl_off = CDNS_MCP_CLK_CTRL1; | ||
| 864 | else | ||
| 865 | mcp_clkctrl_off = CDNS_MCP_CLK_CTRL0; | ||
| 866 | |||
| 867 | mcp_clkctrl = cdns_readl(cdns, mcp_clkctrl_off); | ||
| 868 | mcp_clkctrl |= divider; | ||
| 869 | cdns_writel(cdns, mcp_clkctrl_off, mcp_clkctrl); | ||
| 870 | |||
| 871 | return 0; | ||
| 872 | } | ||
| 873 | EXPORT_SYMBOL(cdns_bus_conf); | ||
| 874 | |||
| 875 | static int cdns_port_params(struct sdw_bus *bus, | ||
| 876 | struct sdw_port_params *p_params, unsigned int bank) | ||
| 877 | { | ||
| 878 | struct sdw_cdns *cdns = bus_to_cdns(bus); | ||
| 879 | int dpn_config = 0, dpn_config_off; | ||
| 880 | |||
| 881 | if (bank) | ||
| 882 | dpn_config_off = CDNS_DPN_B1_CONFIG(p_params->num); | ||
| 883 | else | ||
| 884 | dpn_config_off = CDNS_DPN_B0_CONFIG(p_params->num); | ||
| 885 | |||
| 886 | dpn_config = cdns_readl(cdns, dpn_config_off); | ||
| 887 | |||
| 888 | dpn_config |= ((p_params->bps - 1) << | ||
| 889 | SDW_REG_SHIFT(CDNS_DPN_CONFIG_WL)); | ||
| 890 | dpn_config |= (p_params->flow_mode << | ||
| 891 | SDW_REG_SHIFT(CDNS_DPN_CONFIG_PORT_FLOW)); | ||
| 892 | dpn_config |= (p_params->data_mode << | ||
| 893 | SDW_REG_SHIFT(CDNS_DPN_CONFIG_PORT_DAT)); | ||
| 894 | |||
| 895 | cdns_writel(cdns, dpn_config_off, dpn_config); | ||
| 896 | |||
| 897 | return 0; | ||
| 898 | } | ||
| 899 | |||
| 900 | static int cdns_transport_params(struct sdw_bus *bus, | ||
| 901 | struct sdw_transport_params *t_params, | ||
| 902 | enum sdw_reg_bank bank) | ||
| 903 | { | ||
| 904 | struct sdw_cdns *cdns = bus_to_cdns(bus); | ||
| 905 | int dpn_offsetctrl = 0, dpn_offsetctrl_off; | ||
| 906 | int dpn_config = 0, dpn_config_off; | ||
| 907 | int dpn_hctrl = 0, dpn_hctrl_off; | ||
| 908 | int num = t_params->port_num; | ||
| 909 | int dpn_samplectrl_off; | ||
| 910 | |||
| 911 | /* | ||
| 912 | * Note: Only full data port is supported on the Master side for | ||
| 913 | * both PCM and PDM ports. | ||
| 914 | */ | ||
| 915 | |||
| 916 | if (bank) { | ||
| 917 | dpn_config_off = CDNS_DPN_B1_CONFIG(num); | ||
| 918 | dpn_samplectrl_off = CDNS_DPN_B1_SAMPLE_CTRL(num); | ||
| 919 | dpn_hctrl_off = CDNS_DPN_B1_HCTRL(num); | ||
| 920 | dpn_offsetctrl_off = CDNS_DPN_B1_OFFSET_CTRL(num); | ||
| 921 | } else { | ||
| 922 | dpn_config_off = CDNS_DPN_B0_CONFIG(num); | ||
| 923 | dpn_samplectrl_off = CDNS_DPN_B0_SAMPLE_CTRL(num); | ||
| 924 | dpn_hctrl_off = CDNS_DPN_B0_HCTRL(num); | ||
| 925 | dpn_offsetctrl_off = CDNS_DPN_B0_OFFSET_CTRL(num); | ||
| 926 | } | ||
| 927 | |||
| 928 | dpn_config = cdns_readl(cdns, dpn_config_off); | ||
| 929 | |||
| 930 | dpn_config |= (t_params->blk_grp_ctrl << | ||
| 931 | SDW_REG_SHIFT(CDNS_DPN_CONFIG_BGC)); | ||
| 932 | dpn_config |= (t_params->blk_pkg_mode << | ||
| 933 | SDW_REG_SHIFT(CDNS_DPN_CONFIG_BPM)); | ||
| 934 | cdns_writel(cdns, dpn_config_off, dpn_config); | ||
| 935 | |||
| 936 | dpn_offsetctrl |= (t_params->offset1 << | ||
| 937 | SDW_REG_SHIFT(CDNS_DPN_OFFSET_CTRL_1)); | ||
| 938 | dpn_offsetctrl |= (t_params->offset2 << | ||
| 939 | SDW_REG_SHIFT(CDNS_DPN_OFFSET_CTRL_2)); | ||
| 940 | cdns_writel(cdns, dpn_offsetctrl_off, dpn_offsetctrl); | ||
| 941 | |||
| 942 | dpn_hctrl |= (t_params->hstart << | ||
| 943 | SDW_REG_SHIFT(CDNS_DPN_HCTRL_HSTART)); | ||
| 944 | dpn_hctrl |= (t_params->hstop << SDW_REG_SHIFT(CDNS_DPN_HCTRL_HSTOP)); | ||
| 945 | dpn_hctrl |= (t_params->lane_ctrl << | ||
| 946 | SDW_REG_SHIFT(CDNS_DPN_HCTRL_LCTRL)); | ||
| 947 | |||
| 948 | cdns_writel(cdns, dpn_hctrl_off, dpn_hctrl); | ||
| 949 | cdns_writel(cdns, dpn_samplectrl_off, (t_params->sample_interval - 1)); | ||
| 950 | |||
| 951 | return 0; | ||
| 952 | } | ||
| 953 | |||
| 954 | static int cdns_port_enable(struct sdw_bus *bus, | ||
| 955 | struct sdw_enable_ch *enable_ch, unsigned int bank) | ||
| 956 | { | ||
| 957 | struct sdw_cdns *cdns = bus_to_cdns(bus); | ||
| 958 | int dpn_chnen_off, ch_mask; | ||
| 959 | |||
| 960 | if (bank) | ||
| 961 | dpn_chnen_off = CDNS_DPN_B1_CH_EN(enable_ch->port_num); | ||
| 962 | else | ||
| 963 | dpn_chnen_off = CDNS_DPN_B0_CH_EN(enable_ch->port_num); | ||
| 964 | |||
| 965 | ch_mask = enable_ch->ch_mask * enable_ch->enable; | ||
| 966 | cdns_writel(cdns, dpn_chnen_off, ch_mask); | ||
| 967 | |||
| 968 | return 0; | ||
| 969 | } | ||
| 970 | |||
| 971 | static const struct sdw_master_port_ops cdns_port_ops = { | ||
| 972 | .dpn_set_port_params = cdns_port_params, | ||
| 973 | .dpn_set_port_transport_params = cdns_transport_params, | ||
| 974 | .dpn_port_enable_ch = cdns_port_enable, | ||
| 735 | }; | 975 | }; |
| 736 | EXPORT_SYMBOL(sdw_cdns_master_ops); | ||
| 737 | 976 | ||
| 738 | /** | 977 | /** |
| 739 | * sdw_cdns_probe() - Cadence probe routine | 978 | * sdw_cdns_probe() - Cadence probe routine |
| @@ -742,10 +981,204 @@ EXPORT_SYMBOL(sdw_cdns_master_ops); | |||
| 742 | int sdw_cdns_probe(struct sdw_cdns *cdns) | 981 | int sdw_cdns_probe(struct sdw_cdns *cdns) |
| 743 | { | 982 | { |
| 744 | init_completion(&cdns->tx_complete); | 983 | init_completion(&cdns->tx_complete); |
| 984 | cdns->bus.port_ops = &cdns_port_ops; | ||
| 745 | 985 | ||
| 746 | return 0; | 986 | return 0; |
| 747 | } | 987 | } |
| 748 | EXPORT_SYMBOL(sdw_cdns_probe); | 988 | EXPORT_SYMBOL(sdw_cdns_probe); |
| 749 | 989 | ||
| 990 | int cdns_set_sdw_stream(struct snd_soc_dai *dai, | ||
| 991 | void *stream, bool pcm, int direction) | ||
| 992 | { | ||
| 993 | struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai); | ||
| 994 | struct sdw_cdns_dma_data *dma; | ||
| 995 | |||
| 996 | dma = kzalloc(sizeof(*dma), GFP_KERNEL); | ||
| 997 | if (!dma) | ||
| 998 | return -ENOMEM; | ||
| 999 | |||
| 1000 | if (pcm) | ||
| 1001 | dma->stream_type = SDW_STREAM_PCM; | ||
| 1002 | else | ||
| 1003 | dma->stream_type = SDW_STREAM_PDM; | ||
| 1004 | |||
| 1005 | dma->bus = &cdns->bus; | ||
| 1006 | dma->link_id = cdns->instance; | ||
| 1007 | |||
| 1008 | dma->stream = stream; | ||
| 1009 | |||
| 1010 | if (direction == SNDRV_PCM_STREAM_PLAYBACK) | ||
| 1011 | dai->playback_dma_data = dma; | ||
| 1012 | else | ||
| 1013 | dai->capture_dma_data = dma; | ||
| 1014 | |||
| 1015 | return 0; | ||
| 1016 | } | ||
| 1017 | EXPORT_SYMBOL(cdns_set_sdw_stream); | ||
| 1018 | |||
| 1019 | /** | ||
| 1020 | * cdns_find_pdi() - Find a free PDI | ||
| 1021 | * | ||
| 1022 | * @cdns: Cadence instance | ||
| 1023 | * @num: Number of PDIs | ||
| 1024 | * @pdi: PDI instances | ||
| 1025 | * | ||
| 1026 | * Find and return a free PDI for a given PDI array | ||
| 1027 | */ | ||
| 1028 | static struct sdw_cdns_pdi *cdns_find_pdi(struct sdw_cdns *cdns, | ||
| 1029 | unsigned int num, struct sdw_cdns_pdi *pdi) | ||
| 1030 | { | ||
| 1031 | int i; | ||
| 1032 | |||
| 1033 | for (i = 0; i < num; i++) { | ||
| 1034 | if (pdi[i].assigned == true) | ||
| 1035 | continue; | ||
| 1036 | pdi[i].assigned = true; | ||
| 1037 | return &pdi[i]; | ||
| 1038 | } | ||
| 1039 | |||
| 1040 | return NULL; | ||
| 1041 | } | ||
| 1042 | |||
| 1043 | /** | ||
| 1044 | * sdw_cdns_config_stream: Configure a stream | ||
| 1045 | * | ||
| 1046 | * @cdns: Cadence instance | ||
| 1047 | * @port: Cadence data port | ||
| 1048 | * @ch: Channel count | ||
| 1049 | * @dir: Data direction | ||
| 1050 | * @pdi: PDI to be used | ||
| 1051 | */ | ||
| 1052 | void sdw_cdns_config_stream(struct sdw_cdns *cdns, | ||
| 1053 | struct sdw_cdns_port *port, | ||
| 1054 | u32 ch, u32 dir, struct sdw_cdns_pdi *pdi) | ||
| 1055 | { | ||
| 1056 | u32 offset, val = 0; | ||
| 1057 | |||
| 1058 | if (dir == SDW_DATA_DIR_RX) | ||
| 1059 | val = CDNS_PORTCTRL_DIRN; | ||
| 1060 | |||
| 1061 | offset = CDNS_PORTCTRL + port->num * CDNS_PORT_OFFSET; | ||
| 1062 | cdns_updatel(cdns, offset, CDNS_PORTCTRL_DIRN, val); | ||
| 1063 | |||
| 1064 | val = port->num; | ||
| 1065 | val |= ((1 << ch) - 1) << SDW_REG_SHIFT(CDNS_PDI_CONFIG_CHANNEL); | ||
| 1066 | cdns_writel(cdns, CDNS_PDI_CONFIG(pdi->num), val); | ||
| 1067 | } | ||
| 1068 | EXPORT_SYMBOL(sdw_cdns_config_stream); | ||
| 1069 | |||
| 1070 | /** | ||
| 1071 | * cdns_get_num_pdi() - Get number of PDIs required | ||
| 1072 | * | ||
| 1073 | * @cdns: Cadence instance | ||
| 1074 | * @pdi: PDI to be used | ||
| 1075 | * @num: Number of PDIs | ||
| 1076 | * @ch_count: Channel count | ||
| 1077 | */ | ||
| 1078 | static int cdns_get_num_pdi(struct sdw_cdns *cdns, | ||
| 1079 | struct sdw_cdns_pdi *pdi, | ||
| 1080 | unsigned int num, u32 ch_count) | ||
| 1081 | { | ||
| 1082 | int i, pdis = 0; | ||
| 1083 | |||
| 1084 | for (i = 0; i < num; i++) { | ||
| 1085 | if (pdi[i].assigned == true) | ||
| 1086 | continue; | ||
| 1087 | |||
| 1088 | if (pdi[i].ch_count < ch_count) | ||
| 1089 | ch_count -= pdi[i].ch_count; | ||
| 1090 | else | ||
| 1091 | ch_count = 0; | ||
| 1092 | |||
| 1093 | pdis++; | ||
| 1094 | |||
| 1095 | if (!ch_count) | ||
| 1096 | break; | ||
| 1097 | } | ||
| 1098 | |||
| 1099 | if (ch_count) | ||
| 1100 | return 0; | ||
| 1101 | |||
| 1102 | return pdis; | ||
| 1103 | } | ||
| 1104 | |||
| 1105 | /** | ||
| 1106 | * sdw_cdns_get_stream() - Get stream information | ||
| 1107 | * | ||
| 1108 | * @cdns: Cadence instance | ||
| 1109 | * @stream: Stream to be allocated | ||
| 1110 | * @ch: Channel count | ||
| 1111 | * @dir: Data direction | ||
| 1112 | */ | ||
| 1113 | int sdw_cdns_get_stream(struct sdw_cdns *cdns, | ||
| 1114 | struct sdw_cdns_streams *stream, | ||
| 1115 | u32 ch, u32 dir) | ||
| 1116 | { | ||
| 1117 | int pdis = 0; | ||
| 1118 | |||
| 1119 | if (dir == SDW_DATA_DIR_RX) | ||
| 1120 | pdis = cdns_get_num_pdi(cdns, stream->in, stream->num_in, ch); | ||
| 1121 | else | ||
| 1122 | pdis = cdns_get_num_pdi(cdns, stream->out, stream->num_out, ch); | ||
| 1123 | |||
| 1124 | /* check if we found PDI, else find in bi-directional */ | ||
| 1125 | if (!pdis) | ||
| 1126 | pdis = cdns_get_num_pdi(cdns, stream->bd, stream->num_bd, ch); | ||
| 1127 | |||
| 1128 | return pdis; | ||
| 1129 | } | ||
| 1130 | EXPORT_SYMBOL(sdw_cdns_get_stream); | ||
| 1131 | |||
| 1132 | /** | ||
| 1133 | * sdw_cdns_alloc_stream() - Allocate a stream | ||
| 1134 | * | ||
| 1135 | * @cdns: Cadence instance | ||
| 1136 | * @stream: Stream to be allocated | ||
| 1137 | * @port: Cadence data port | ||
| 1138 | * @ch: Channel count | ||
| 1139 | * @dir: Data direction | ||
| 1140 | */ | ||
| 1141 | int sdw_cdns_alloc_stream(struct sdw_cdns *cdns, | ||
| 1142 | struct sdw_cdns_streams *stream, | ||
| 1143 | struct sdw_cdns_port *port, u32 ch, u32 dir) | ||
| 1144 | { | ||
| 1145 | struct sdw_cdns_pdi *pdi = NULL; | ||
| 1146 | |||
| 1147 | if (dir == SDW_DATA_DIR_RX) | ||
| 1148 | pdi = cdns_find_pdi(cdns, stream->num_in, stream->in); | ||
| 1149 | else | ||
| 1150 | pdi = cdns_find_pdi(cdns, stream->num_out, stream->out); | ||
| 1151 | |||
| 1152 | /* check if we found a PDI, else find in bi-directional */ | ||
| 1153 | if (!pdi) | ||
| 1154 | pdi = cdns_find_pdi(cdns, stream->num_bd, stream->bd); | ||
| 1155 | |||
| 1156 | if (!pdi) | ||
| 1157 | return -EIO; | ||
| 1158 | |||
| 1159 | port->pdi = pdi; | ||
| 1160 | pdi->l_ch_num = 0; | ||
| 1161 | pdi->h_ch_num = ch - 1; | ||
| 1162 | pdi->dir = dir; | ||
| 1163 | pdi->ch_count = ch; | ||
| 1164 | |||
| 1165 | return 0; | ||
| 1166 | } | ||
| 1167 | EXPORT_SYMBOL(sdw_cdns_alloc_stream); | ||
| 1168 | |||
| 1169 | void sdw_cdns_shutdown(struct snd_pcm_substream *substream, | ||
| 1170 | struct snd_soc_dai *dai) | ||
| 1171 | { | ||
| 1172 | struct sdw_cdns_dma_data *dma; | ||
| 1173 | |||
| 1174 | dma = snd_soc_dai_get_dma_data(dai, substream); | ||
| 1175 | if (!dma) | ||
| 1176 | return; | ||
| 1177 | |||
| 1178 | snd_soc_dai_set_dma_data(dai, substream, NULL); | ||
| 1179 | kfree(dma); | ||
| 1180 | } | ||
| 1181 | EXPORT_SYMBOL(sdw_cdns_shutdown); | ||
| 1182 | |||
| 750 | MODULE_LICENSE("Dual BSD/GPL"); | 1183 | MODULE_LICENSE("Dual BSD/GPL"); |
| 751 | MODULE_DESCRIPTION("Cadence Soundwire Library"); | 1184 | MODULE_DESCRIPTION("Cadence Soundwire Library"); |
diff --git a/drivers/soundwire/cadence_master.h b/drivers/soundwire/cadence_master.h index beaf6c9804eb..eb902b19c5a4 100644 --- a/drivers/soundwire/cadence_master.h +++ b/drivers/soundwire/cadence_master.h | |||
| @@ -1,10 +1,117 @@ | |||
| 1 | // SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) | 1 | // SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) |
| 2 | // Copyright(c) 2015-17 Intel Corporation. | 2 | // Copyright(c) 2015-17 Intel Corporation. |
| 3 | #include <sound/soc.h> | ||
| 3 | 4 | ||
| 4 | #ifndef __SDW_CADENCE_H | 5 | #ifndef __SDW_CADENCE_H |
| 5 | #define __SDW_CADENCE_H | 6 | #define __SDW_CADENCE_H |
| 6 | 7 | ||
| 7 | /** | 8 | /** |
| 9 | * struct sdw_cdns_pdi: PDI (Physical Data Interface) instance | ||
| 10 | * | ||
| 11 | * @assigned: pdi assigned | ||
| 12 | * @num: pdi number | ||
| 13 | * @intel_alh_id: link identifier | ||
| 14 | * @l_ch_num: low channel for PDI | ||
| 15 | * @h_ch_num: high channel for PDI | ||
| 16 | * @ch_count: total channel count for PDI | ||
| 17 | * @dir: data direction | ||
| 18 | * @type: stream type, PDM or PCM | ||
| 19 | */ | ||
| 20 | struct sdw_cdns_pdi { | ||
| 21 | bool assigned; | ||
| 22 | int num; | ||
| 23 | int intel_alh_id; | ||
| 24 | int l_ch_num; | ||
| 25 | int h_ch_num; | ||
| 26 | int ch_count; | ||
| 27 | enum sdw_data_direction dir; | ||
| 28 | enum sdw_stream_type type; | ||
| 29 | }; | ||
| 30 | |||
| 31 | /** | ||
| 32 | * struct sdw_cdns_port: Cadence port structure | ||
| 33 | * | ||
| 34 | * @num: port number | ||
| 35 | * @assigned: port assigned | ||
| 36 | * @ch: channel count | ||
| 37 | * @direction: data port direction | ||
| 38 | * @pdi: pdi for this port | ||
| 39 | */ | ||
| 40 | struct sdw_cdns_port { | ||
| 41 | unsigned int num; | ||
| 42 | bool assigned; | ||
| 43 | unsigned int ch; | ||
| 44 | enum sdw_data_direction direction; | ||
| 45 | struct sdw_cdns_pdi *pdi; | ||
| 46 | }; | ||
| 47 | |||
| 48 | /** | ||
| 49 | * struct sdw_cdns_streams: Cadence stream data structure | ||
| 50 | * | ||
| 51 | * @num_bd: number of bidirectional streams | ||
| 52 | * @num_in: number of input streams | ||
| 53 | * @num_out: number of output streams | ||
| 54 | * @num_ch_bd: number of bidirectional stream channels | ||
| 55 | * @num_ch_bd: number of input stream channels | ||
| 56 | * @num_ch_bd: number of output stream channels | ||
| 57 | * @num_pdi: total number of PDIs | ||
| 58 | * @bd: bidirectional streams | ||
| 59 | * @in: input streams | ||
| 60 | * @out: output streams | ||
| 61 | */ | ||
| 62 | struct sdw_cdns_streams { | ||
| 63 | unsigned int num_bd; | ||
| 64 | unsigned int num_in; | ||
| 65 | unsigned int num_out; | ||
| 66 | unsigned int num_ch_bd; | ||
| 67 | unsigned int num_ch_in; | ||
| 68 | unsigned int num_ch_out; | ||
| 69 | unsigned int num_pdi; | ||
| 70 | struct sdw_cdns_pdi *bd; | ||
| 71 | struct sdw_cdns_pdi *in; | ||
| 72 | struct sdw_cdns_pdi *out; | ||
| 73 | }; | ||
| 74 | |||
| 75 | /** | ||
| 76 | * struct sdw_cdns_stream_config: stream configuration | ||
| 77 | * | ||
| 78 | * @pcm_bd: number of bidirectional PCM streams supported | ||
| 79 | * @pcm_in: number of input PCM streams supported | ||
| 80 | * @pcm_out: number of output PCM streams supported | ||
| 81 | * @pdm_bd: number of bidirectional PDM streams supported | ||
| 82 | * @pdm_in: number of input PDM streams supported | ||
| 83 | * @pdm_out: number of output PDM streams supported | ||
| 84 | */ | ||
| 85 | struct sdw_cdns_stream_config { | ||
| 86 | unsigned int pcm_bd; | ||
| 87 | unsigned int pcm_in; | ||
| 88 | unsigned int pcm_out; | ||
| 89 | unsigned int pdm_bd; | ||
| 90 | unsigned int pdm_in; | ||
| 91 | unsigned int pdm_out; | ||
| 92 | }; | ||
| 93 | |||
| 94 | /** | ||
| 95 | * struct sdw_cdns_dma_data: Cadence DMA data | ||
| 96 | * | ||
| 97 | * @name: SoundWire stream name | ||
| 98 | * @nr_ports: Number of ports | ||
| 99 | * @port: Ports | ||
| 100 | * @bus: Bus handle | ||
| 101 | * @stream_type: Stream type | ||
| 102 | * @link_id: Master link id | ||
| 103 | */ | ||
| 104 | struct sdw_cdns_dma_data { | ||
| 105 | char *name; | ||
| 106 | struct sdw_stream_runtime *stream; | ||
| 107 | int nr_ports; | ||
| 108 | struct sdw_cdns_port **port; | ||
| 109 | struct sdw_bus *bus; | ||
| 110 | enum sdw_stream_type stream_type; | ||
| 111 | int link_id; | ||
| 112 | }; | ||
| 113 | |||
| 114 | /** | ||
| 8 | * struct sdw_cdns - Cadence driver context | 115 | * struct sdw_cdns - Cadence driver context |
| 9 | * @dev: Linux device | 116 | * @dev: Linux device |
| 10 | * @bus: Bus handle | 117 | * @bus: Bus handle |
| @@ -12,6 +119,10 @@ | |||
| 12 | * @response_buf: SoundWire response buffer | 119 | * @response_buf: SoundWire response buffer |
| 13 | * @tx_complete: Tx completion | 120 | * @tx_complete: Tx completion |
| 14 | * @defer: Defer pointer | 121 | * @defer: Defer pointer |
| 122 | * @ports: Data ports | ||
| 123 | * @num_ports: Total number of data ports | ||
| 124 | * @pcm: PCM streams | ||
| 125 | * @pdm: PDM streams | ||
| 15 | * @registers: Cadence registers | 126 | * @registers: Cadence registers |
| 16 | * @link_up: Link status | 127 | * @link_up: Link status |
| 17 | * @msg_count: Messages sent on bus | 128 | * @msg_count: Messages sent on bus |
| @@ -25,6 +136,12 @@ struct sdw_cdns { | |||
| 25 | struct completion tx_complete; | 136 | struct completion tx_complete; |
| 26 | struct sdw_defer *defer; | 137 | struct sdw_defer *defer; |
| 27 | 138 | ||
| 139 | struct sdw_cdns_port *ports; | ||
| 140 | int num_ports; | ||
| 141 | |||
| 142 | struct sdw_cdns_streams pcm; | ||
| 143 | struct sdw_cdns_streams pdm; | ||
| 144 | |||
| 28 | void __iomem *registers; | 145 | void __iomem *registers; |
| 29 | 146 | ||
| 30 | bool link_up; | 147 | bool link_up; |
| @@ -42,7 +159,41 @@ irqreturn_t sdw_cdns_irq(int irq, void *dev_id); | |||
| 42 | irqreturn_t sdw_cdns_thread(int irq, void *dev_id); | 159 | irqreturn_t sdw_cdns_thread(int irq, void *dev_id); |
| 43 | 160 | ||
| 44 | int sdw_cdns_init(struct sdw_cdns *cdns); | 161 | int sdw_cdns_init(struct sdw_cdns *cdns); |
| 162 | int sdw_cdns_pdi_init(struct sdw_cdns *cdns, | ||
| 163 | struct sdw_cdns_stream_config config); | ||
| 45 | int sdw_cdns_enable_interrupt(struct sdw_cdns *cdns); | 164 | int sdw_cdns_enable_interrupt(struct sdw_cdns *cdns); |
| 46 | 165 | ||
| 166 | int sdw_cdns_get_stream(struct sdw_cdns *cdns, | ||
| 167 | struct sdw_cdns_streams *stream, | ||
| 168 | u32 ch, u32 dir); | ||
| 169 | int sdw_cdns_alloc_stream(struct sdw_cdns *cdns, | ||
| 170 | struct sdw_cdns_streams *stream, | ||
| 171 | struct sdw_cdns_port *port, u32 ch, u32 dir); | ||
| 172 | void sdw_cdns_config_stream(struct sdw_cdns *cdns, struct sdw_cdns_port *port, | ||
| 173 | u32 ch, u32 dir, struct sdw_cdns_pdi *pdi); | ||
| 174 | |||
| 175 | void sdw_cdns_shutdown(struct snd_pcm_substream *substream, | ||
| 176 | struct snd_soc_dai *dai); | ||
| 177 | int sdw_cdns_pcm_set_stream(struct snd_soc_dai *dai, | ||
| 178 | void *stream, int direction); | ||
| 179 | int sdw_cdns_pdm_set_stream(struct snd_soc_dai *dai, | ||
| 180 | void *stream, int direction); | ||
| 181 | |||
| 182 | enum sdw_command_response | ||
| 183 | cdns_reset_page_addr(struct sdw_bus *bus, unsigned int dev_num); | ||
| 184 | |||
| 185 | enum sdw_command_response | ||
| 186 | cdns_xfer_msg(struct sdw_bus *bus, struct sdw_msg *msg); | ||
| 187 | |||
| 188 | enum sdw_command_response | ||
| 189 | cdns_xfer_msg_defer(struct sdw_bus *bus, | ||
| 190 | struct sdw_msg *msg, struct sdw_defer *defer); | ||
| 191 | |||
| 192 | enum sdw_command_response | ||
| 193 | cdns_reset_page_addr(struct sdw_bus *bus, unsigned int dev_num); | ||
| 194 | |||
| 195 | int cdns_bus_conf(struct sdw_bus *bus, struct sdw_bus_params *params); | ||
| 47 | 196 | ||
| 197 | int cdns_set_sdw_stream(struct snd_soc_dai *dai, | ||
| 198 | void *stream, bool pcm, int direction); | ||
| 48 | #endif /* __SDW_CADENCE_H */ | 199 | #endif /* __SDW_CADENCE_H */ |
diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index 86a7bd1fc912..0a8990e758f9 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c | |||
| @@ -9,6 +9,8 @@ | |||
| 9 | #include <linux/delay.h> | 9 | #include <linux/delay.h> |
| 10 | #include <linux/interrupt.h> | 10 | #include <linux/interrupt.h> |
| 11 | #include <linux/platform_device.h> | 11 | #include <linux/platform_device.h> |
| 12 | #include <sound/pcm_params.h> | ||
| 13 | #include <sound/soc.h> | ||
| 12 | #include <linux/soundwire/sdw_registers.h> | 14 | #include <linux/soundwire/sdw_registers.h> |
| 13 | #include <linux/soundwire/sdw.h> | 15 | #include <linux/soundwire/sdw.h> |
| 14 | #include <linux/soundwire/sdw_intel.h> | 16 | #include <linux/soundwire/sdw_intel.h> |
| @@ -85,6 +87,12 @@ | |||
| 85 | #define SDW_ALH_STRMZCFG_DMAT GENMASK(7, 0) | 87 | #define SDW_ALH_STRMZCFG_DMAT GENMASK(7, 0) |
| 86 | #define SDW_ALH_STRMZCFG_CHN GENMASK(19, 16) | 88 | #define SDW_ALH_STRMZCFG_CHN GENMASK(19, 16) |
| 87 | 89 | ||
| 90 | enum intel_pdi_type { | ||
| 91 | INTEL_PDI_IN = 0, | ||
| 92 | INTEL_PDI_OUT = 1, | ||
| 93 | INTEL_PDI_BD = 2, | ||
| 94 | }; | ||
| 95 | |||
| 88 | struct sdw_intel { | 96 | struct sdw_intel { |
| 89 | struct sdw_cdns cdns; | 97 | struct sdw_cdns cdns; |
| 90 | int instance; | 98 | int instance; |
| @@ -234,6 +242,490 @@ static int intel_shim_init(struct sdw_intel *sdw) | |||
| 234 | return ret; | 242 | return ret; |
| 235 | } | 243 | } |
| 236 | 244 | ||
| 245 | /* | ||
| 246 | * PDI routines | ||
| 247 | */ | ||
| 248 | static void intel_pdi_init(struct sdw_intel *sdw, | ||
| 249 | struct sdw_cdns_stream_config *config) | ||
| 250 | { | ||
| 251 | void __iomem *shim = sdw->res->shim; | ||
| 252 | unsigned int link_id = sdw->instance; | ||
| 253 | int pcm_cap, pdm_cap; | ||
| 254 | |||
| 255 | /* PCM Stream Capability */ | ||
| 256 | pcm_cap = intel_readw(shim, SDW_SHIM_PCMSCAP(link_id)); | ||
| 257 | |||
| 258 | config->pcm_bd = (pcm_cap & SDW_SHIM_PCMSCAP_BSS) >> | ||
| 259 | SDW_REG_SHIFT(SDW_SHIM_PCMSCAP_BSS); | ||
| 260 | config->pcm_in = (pcm_cap & SDW_SHIM_PCMSCAP_ISS) >> | ||
| 261 | SDW_REG_SHIFT(SDW_SHIM_PCMSCAP_ISS); | ||
| 262 | config->pcm_out = (pcm_cap & SDW_SHIM_PCMSCAP_OSS) >> | ||
| 263 | SDW_REG_SHIFT(SDW_SHIM_PCMSCAP_OSS); | ||
| 264 | |||
| 265 | /* PDM Stream Capability */ | ||
| 266 | pdm_cap = intel_readw(shim, SDW_SHIM_PDMSCAP(link_id)); | ||
| 267 | |||
| 268 | config->pdm_bd = (pdm_cap & SDW_SHIM_PDMSCAP_BSS) >> | ||
| 269 | SDW_REG_SHIFT(SDW_SHIM_PDMSCAP_BSS); | ||
| 270 | config->pdm_in = (pdm_cap & SDW_SHIM_PDMSCAP_ISS) >> | ||
| 271 | SDW_REG_SHIFT(SDW_SHIM_PDMSCAP_ISS); | ||
| 272 | config->pdm_out = (pdm_cap & SDW_SHIM_PDMSCAP_OSS) >> | ||
| 273 | SDW_REG_SHIFT(SDW_SHIM_PDMSCAP_OSS); | ||
| 274 | } | ||
| 275 | |||
| 276 | static int | ||
| 277 | intel_pdi_get_ch_cap(struct sdw_intel *sdw, unsigned int pdi_num, bool pcm) | ||
| 278 | { | ||
| 279 | void __iomem *shim = sdw->res->shim; | ||
| 280 | unsigned int link_id = sdw->instance; | ||
| 281 | int count; | ||
| 282 | |||
| 283 | if (pcm) { | ||
| 284 | count = intel_readw(shim, SDW_SHIM_PCMSYCHC(link_id, pdi_num)); | ||
| 285 | } else { | ||
| 286 | count = intel_readw(shim, SDW_SHIM_PDMSCAP(link_id)); | ||
| 287 | count = ((count & SDW_SHIM_PDMSCAP_CPSS) >> | ||
| 288 | SDW_REG_SHIFT(SDW_SHIM_PDMSCAP_CPSS)); | ||
| 289 | } | ||
| 290 | |||
| 291 | /* zero based values for channel count in register */ | ||
| 292 | count++; | ||
| 293 | |||
| 294 | return count; | ||
| 295 | } | ||
| 296 | |||
| 297 | static int intel_pdi_get_ch_update(struct sdw_intel *sdw, | ||
| 298 | struct sdw_cdns_pdi *pdi, | ||
| 299 | unsigned int num_pdi, | ||
| 300 | unsigned int *num_ch, bool pcm) | ||
| 301 | { | ||
| 302 | int i, ch_count = 0; | ||
| 303 | |||
| 304 | for (i = 0; i < num_pdi; i++) { | ||
| 305 | pdi->ch_count = intel_pdi_get_ch_cap(sdw, pdi->num, pcm); | ||
| 306 | ch_count += pdi->ch_count; | ||
| 307 | pdi++; | ||
| 308 | } | ||
| 309 | |||
| 310 | *num_ch = ch_count; | ||
| 311 | return 0; | ||
| 312 | } | ||
| 313 | |||
| 314 | static int intel_pdi_stream_ch_update(struct sdw_intel *sdw, | ||
| 315 | struct sdw_cdns_streams *stream, bool pcm) | ||
| 316 | { | ||
| 317 | intel_pdi_get_ch_update(sdw, stream->bd, stream->num_bd, | ||
| 318 | &stream->num_ch_bd, pcm); | ||
| 319 | |||
| 320 | intel_pdi_get_ch_update(sdw, stream->in, stream->num_in, | ||
| 321 | &stream->num_ch_in, pcm); | ||
| 322 | |||
| 323 | intel_pdi_get_ch_update(sdw, stream->out, stream->num_out, | ||
| 324 | &stream->num_ch_out, pcm); | ||
| 325 | |||
| 326 | return 0; | ||
| 327 | } | ||
| 328 | |||
| 329 | static int intel_pdi_ch_update(struct sdw_intel *sdw) | ||
| 330 | { | ||
| 331 | /* First update PCM streams followed by PDM streams */ | ||
| 332 | intel_pdi_stream_ch_update(sdw, &sdw->cdns.pcm, true); | ||
| 333 | intel_pdi_stream_ch_update(sdw, &sdw->cdns.pdm, false); | ||
| 334 | |||
| 335 | return 0; | ||
| 336 | } | ||
| 337 | |||
| 338 | static void | ||
| 339 | intel_pdi_shim_configure(struct sdw_intel *sdw, struct sdw_cdns_pdi *pdi) | ||
| 340 | { | ||
| 341 | void __iomem *shim = sdw->res->shim; | ||
| 342 | unsigned int link_id = sdw->instance; | ||
| 343 | int pdi_conf = 0; | ||
| 344 | |||
| 345 | pdi->intel_alh_id = (link_id * 16) + pdi->num + 5; | ||
| 346 | |||
| 347 | /* | ||
| 348 | * Program stream parameters to stream SHIM register | ||
| 349 | * This is applicable for PCM stream only. | ||
| 350 | */ | ||
| 351 | if (pdi->type != SDW_STREAM_PCM) | ||
| 352 | return; | ||
| 353 | |||
| 354 | if (pdi->dir == SDW_DATA_DIR_RX) | ||
| 355 | pdi_conf |= SDW_SHIM_PCMSYCM_DIR; | ||
| 356 | else | ||
| 357 | pdi_conf &= ~(SDW_SHIM_PCMSYCM_DIR); | ||
| 358 | |||
| 359 | pdi_conf |= (pdi->intel_alh_id << | ||
| 360 | SDW_REG_SHIFT(SDW_SHIM_PCMSYCM_STREAM)); | ||
| 361 | pdi_conf |= (pdi->l_ch_num << SDW_REG_SHIFT(SDW_SHIM_PCMSYCM_LCHN)); | ||
| 362 | pdi_conf |= (pdi->h_ch_num << SDW_REG_SHIFT(SDW_SHIM_PCMSYCM_HCHN)); | ||
| 363 | |||
| 364 | intel_writew(shim, SDW_SHIM_PCMSYCHM(link_id, pdi->num), pdi_conf); | ||
| 365 | } | ||
| 366 | |||
| 367 | static void | ||
| 368 | intel_pdi_alh_configure(struct sdw_intel *sdw, struct sdw_cdns_pdi *pdi) | ||
| 369 | { | ||
| 370 | void __iomem *alh = sdw->res->alh; | ||
| 371 | unsigned int link_id = sdw->instance; | ||
| 372 | unsigned int conf; | ||
| 373 | |||
| 374 | pdi->intel_alh_id = (link_id * 16) + pdi->num + 5; | ||
| 375 | |||
| 376 | /* Program Stream config ALH register */ | ||
| 377 | conf = intel_readl(alh, SDW_ALH_STRMZCFG(pdi->intel_alh_id)); | ||
| 378 | |||
| 379 | conf |= (SDW_ALH_STRMZCFG_DMAT_VAL << | ||
| 380 | SDW_REG_SHIFT(SDW_ALH_STRMZCFG_DMAT)); | ||
| 381 | |||
| 382 | conf |= ((pdi->ch_count - 1) << | ||
| 383 | SDW_REG_SHIFT(SDW_ALH_STRMZCFG_CHN)); | ||
| 384 | |||
| 385 | intel_writel(alh, SDW_ALH_STRMZCFG(pdi->intel_alh_id), conf); | ||
| 386 | } | ||
| 387 | |||
| 388 | static int intel_config_stream(struct sdw_intel *sdw, | ||
| 389 | struct snd_pcm_substream *substream, | ||
| 390 | struct snd_soc_dai *dai, | ||
| 391 | struct snd_pcm_hw_params *hw_params, int link_id) | ||
| 392 | { | ||
| 393 | if (sdw->res->ops && sdw->res->ops->config_stream) | ||
| 394 | return sdw->res->ops->config_stream(sdw->res->arg, | ||
| 395 | substream, dai, hw_params, link_id); | ||
| 396 | |||
| 397 | return -EIO; | ||
| 398 | } | ||
| 399 | |||
| 400 | /* | ||
| 401 | * DAI routines | ||
| 402 | */ | ||
| 403 | |||
| 404 | static struct sdw_cdns_port *intel_alloc_port(struct sdw_intel *sdw, | ||
| 405 | u32 ch, u32 dir, bool pcm) | ||
| 406 | { | ||
| 407 | struct sdw_cdns *cdns = &sdw->cdns; | ||
| 408 | struct sdw_cdns_port *port = NULL; | ||
| 409 | int i, ret = 0; | ||
| 410 | |||
| 411 | for (i = 0; i < cdns->num_ports; i++) { | ||
| 412 | if (cdns->ports[i].assigned == true) | ||
| 413 | continue; | ||
| 414 | |||
| 415 | port = &cdns->ports[i]; | ||
| 416 | port->assigned = true; | ||
| 417 | port->direction = dir; | ||
| 418 | port->ch = ch; | ||
| 419 | break; | ||
| 420 | } | ||
| 421 | |||
| 422 | if (!port) { | ||
| 423 | dev_err(cdns->dev, "Unable to find a free port\n"); | ||
| 424 | return NULL; | ||
| 425 | } | ||
| 426 | |||
| 427 | if (pcm) { | ||
| 428 | ret = sdw_cdns_alloc_stream(cdns, &cdns->pcm, port, ch, dir); | ||
| 429 | if (ret) | ||
| 430 | goto out; | ||
| 431 | |||
| 432 | intel_pdi_shim_configure(sdw, port->pdi); | ||
| 433 | sdw_cdns_config_stream(cdns, port, ch, dir, port->pdi); | ||
| 434 | |||
| 435 | intel_pdi_alh_configure(sdw, port->pdi); | ||
| 436 | |||
| 437 | } else { | ||
| 438 | ret = sdw_cdns_alloc_stream(cdns, &cdns->pdm, port, ch, dir); | ||
| 439 | } | ||
| 440 | |||
| 441 | out: | ||
| 442 | if (ret) { | ||
| 443 | port->assigned = false; | ||
| 444 | port = NULL; | ||
| 445 | } | ||
| 446 | |||
| 447 | return port; | ||
| 448 | } | ||
| 449 | |||
| 450 | static void intel_port_cleanup(struct sdw_cdns_dma_data *dma) | ||
| 451 | { | ||
| 452 | int i; | ||
| 453 | |||
| 454 | for (i = 0; i < dma->nr_ports; i++) { | ||
| 455 | if (dma->port[i]) { | ||
| 456 | dma->port[i]->pdi->assigned = false; | ||
| 457 | dma->port[i]->pdi = NULL; | ||
| 458 | dma->port[i]->assigned = false; | ||
| 459 | dma->port[i] = NULL; | ||
| 460 | } | ||
| 461 | } | ||
| 462 | } | ||
| 463 | |||
| 464 | static int intel_hw_params(struct snd_pcm_substream *substream, | ||
| 465 | struct snd_pcm_hw_params *params, | ||
| 466 | struct snd_soc_dai *dai) | ||
| 467 | { | ||
| 468 | struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai); | ||
| 469 | struct sdw_intel *sdw = cdns_to_intel(cdns); | ||
| 470 | struct sdw_cdns_dma_data *dma; | ||
| 471 | struct sdw_stream_config sconfig; | ||
| 472 | struct sdw_port_config *pconfig; | ||
| 473 | int ret, i, ch, dir; | ||
| 474 | bool pcm = true; | ||
| 475 | |||
| 476 | dma = snd_soc_dai_get_dma_data(dai, substream); | ||
| 477 | if (!dma) | ||
| 478 | return -EIO; | ||
| 479 | |||
| 480 | ch = params_channels(params); | ||
| 481 | if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) | ||
| 482 | dir = SDW_DATA_DIR_RX; | ||
| 483 | else | ||
| 484 | dir = SDW_DATA_DIR_TX; | ||
| 485 | |||
| 486 | if (dma->stream_type == SDW_STREAM_PDM) { | ||
| 487 | /* TODO: Check whether PDM decimator is already in use */ | ||
| 488 | dma->nr_ports = sdw_cdns_get_stream(cdns, &cdns->pdm, ch, dir); | ||
| 489 | pcm = false; | ||
| 490 | } else { | ||
| 491 | dma->nr_ports = sdw_cdns_get_stream(cdns, &cdns->pcm, ch, dir); | ||
| 492 | } | ||
| 493 | |||
| 494 | if (!dma->nr_ports) { | ||
| 495 | dev_err(dai->dev, "ports/resources not available"); | ||
| 496 | return -EINVAL; | ||
| 497 | } | ||
| 498 | |||
| 499 | dma->port = kcalloc(dma->nr_ports, sizeof(*dma->port), GFP_KERNEL); | ||
| 500 | if (!dma->port) | ||
| 501 | return -ENOMEM; | ||
| 502 | |||
| 503 | for (i = 0; i < dma->nr_ports; i++) { | ||
| 504 | dma->port[i] = intel_alloc_port(sdw, ch, dir, pcm); | ||
| 505 | if (!dma->port[i]) { | ||
| 506 | ret = -EINVAL; | ||
| 507 | goto port_error; | ||
| 508 | } | ||
| 509 | } | ||
| 510 | |||
| 511 | /* Inform DSP about PDI stream number */ | ||
| 512 | for (i = 0; i < dma->nr_ports; i++) { | ||
| 513 | ret = intel_config_stream(sdw, substream, dai, params, | ||
| 514 | dma->port[i]->pdi->intel_alh_id); | ||
| 515 | if (ret) | ||
| 516 | goto port_error; | ||
| 517 | } | ||
| 518 | |||
| 519 | sconfig.direction = dir; | ||
| 520 | sconfig.ch_count = ch; | ||
| 521 | sconfig.frame_rate = params_rate(params); | ||
| 522 | sconfig.type = dma->stream_type; | ||
| 523 | |||
| 524 | if (dma->stream_type == SDW_STREAM_PDM) { | ||
| 525 | sconfig.frame_rate *= 50; | ||
| 526 | sconfig.bps = 1; | ||
| 527 | } else { | ||
| 528 | sconfig.bps = snd_pcm_format_width(params_format(params)); | ||
| 529 | } | ||
| 530 | |||
| 531 | /* Port configuration */ | ||
| 532 | pconfig = kcalloc(dma->nr_ports, sizeof(*pconfig), GFP_KERNEL); | ||
| 533 | if (!pconfig) { | ||
| 534 | ret = -ENOMEM; | ||
| 535 | goto port_error; | ||
| 536 | } | ||
| 537 | |||
| 538 | for (i = 0; i < dma->nr_ports; i++) { | ||
| 539 | pconfig[i].num = dma->port[i]->num; | ||
| 540 | pconfig[i].ch_mask = (1 << ch) - 1; | ||
| 541 | } | ||
| 542 | |||
| 543 | ret = sdw_stream_add_master(&cdns->bus, &sconfig, | ||
| 544 | pconfig, dma->nr_ports, dma->stream); | ||
| 545 | if (ret) { | ||
| 546 | dev_err(cdns->dev, "add master to stream failed:%d", ret); | ||
| 547 | goto stream_error; | ||
| 548 | } | ||
| 549 | |||
| 550 | kfree(pconfig); | ||
| 551 | return ret; | ||
| 552 | |||
| 553 | stream_error: | ||
| 554 | kfree(pconfig); | ||
| 555 | port_error: | ||
| 556 | intel_port_cleanup(dma); | ||
| 557 | kfree(dma->port); | ||
| 558 | return ret; | ||
| 559 | } | ||
| 560 | |||
| 561 | static int | ||
| 562 | intel_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) | ||
| 563 | { | ||
| 564 | struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai); | ||
| 565 | struct sdw_cdns_dma_data *dma; | ||
| 566 | int ret; | ||
| 567 | |||
| 568 | dma = snd_soc_dai_get_dma_data(dai, substream); | ||
| 569 | if (!dma) | ||
| 570 | return -EIO; | ||
| 571 | |||
| 572 | ret = sdw_stream_remove_master(&cdns->bus, dma->stream); | ||
| 573 | if (ret < 0) | ||
| 574 | dev_err(dai->dev, "remove master from stream %s failed: %d", | ||
| 575 | dma->stream->name, ret); | ||
| 576 | |||
| 577 | intel_port_cleanup(dma); | ||
| 578 | kfree(dma->port); | ||
| 579 | return ret; | ||
| 580 | } | ||
| 581 | |||
| 582 | static int intel_pcm_set_sdw_stream(struct snd_soc_dai *dai, | ||
| 583 | void *stream, int direction) | ||
| 584 | { | ||
| 585 | return cdns_set_sdw_stream(dai, stream, true, direction); | ||
| 586 | } | ||
| 587 | |||
| 588 | static int intel_pdm_set_sdw_stream(struct snd_soc_dai *dai, | ||
| 589 | void *stream, int direction) | ||
| 590 | { | ||
| 591 | return cdns_set_sdw_stream(dai, stream, false, direction); | ||
| 592 | } | ||
| 593 | |||
| 594 | static struct snd_soc_dai_ops intel_pcm_dai_ops = { | ||
| 595 | .hw_params = intel_hw_params, | ||
| 596 | .hw_free = intel_hw_free, | ||
| 597 | .shutdown = sdw_cdns_shutdown, | ||
| 598 | .set_sdw_stream = intel_pcm_set_sdw_stream, | ||
| 599 | }; | ||
| 600 | |||
| 601 | static struct snd_soc_dai_ops intel_pdm_dai_ops = { | ||
| 602 | .hw_params = intel_hw_params, | ||
| 603 | .hw_free = intel_hw_free, | ||
| 604 | .shutdown = sdw_cdns_shutdown, | ||
| 605 | .set_sdw_stream = intel_pdm_set_sdw_stream, | ||
| 606 | }; | ||
| 607 | |||
| 608 | static const struct snd_soc_component_driver dai_component = { | ||
| 609 | .name = "soundwire", | ||
| 610 | }; | ||
| 611 | |||
| 612 | static int intel_create_dai(struct sdw_cdns *cdns, | ||
| 613 | struct snd_soc_dai_driver *dais, | ||
| 614 | enum intel_pdi_type type, | ||
| 615 | u32 num, u32 off, u32 max_ch, bool pcm) | ||
| 616 | { | ||
| 617 | int i; | ||
| 618 | |||
| 619 | if (num == 0) | ||
| 620 | return 0; | ||
| 621 | |||
| 622 | /* TODO: Read supported rates/formats from hardware */ | ||
| 623 | for (i = off; i < (off + num); i++) { | ||
| 624 | dais[i].name = kasprintf(GFP_KERNEL, "SDW%d Pin%d", | ||
| 625 | cdns->instance, i); | ||
| 626 | if (!dais[i].name) | ||
| 627 | return -ENOMEM; | ||
| 628 | |||
| 629 | if (type == INTEL_PDI_BD || type == INTEL_PDI_OUT) { | ||
| 630 | dais[i].playback.stream_name = kasprintf(GFP_KERNEL, | ||
| 631 | "SDW%d Tx%d", | ||
| 632 | cdns->instance, i); | ||
| 633 | if (!dais[i].playback.stream_name) { | ||
| 634 | kfree(dais[i].name); | ||
| 635 | return -ENOMEM; | ||
| 636 | } | ||
| 637 | |||
| 638 | dais[i].playback.channels_min = 1; | ||
| 639 | dais[i].playback.channels_max = max_ch; | ||
| 640 | dais[i].playback.rates = SNDRV_PCM_RATE_48000; | ||
| 641 | dais[i].playback.formats = SNDRV_PCM_FMTBIT_S16_LE; | ||
| 642 | } | ||
| 643 | |||
| 644 | if (type == INTEL_PDI_BD || type == INTEL_PDI_IN) { | ||
| 645 | dais[i].capture.stream_name = kasprintf(GFP_KERNEL, | ||
| 646 | "SDW%d Rx%d", | ||
| 647 | cdns->instance, i); | ||
| 648 | if (!dais[i].capture.stream_name) { | ||
| 649 | kfree(dais[i].name); | ||
| 650 | kfree(dais[i].playback.stream_name); | ||
| 651 | return -ENOMEM; | ||
| 652 | } | ||
| 653 | |||
| 654 | dais[i].playback.channels_min = 1; | ||
| 655 | dais[i].playback.channels_max = max_ch; | ||
| 656 | dais[i].capture.rates = SNDRV_PCM_RATE_48000; | ||
| 657 | dais[i].capture.formats = SNDRV_PCM_FMTBIT_S16_LE; | ||
| 658 | } | ||
| 659 | |||
| 660 | dais[i].id = SDW_DAI_ID_RANGE_START + i; | ||
| 661 | |||
| 662 | if (pcm) | ||
| 663 | dais[i].ops = &intel_pcm_dai_ops; | ||
| 664 | else | ||
| 665 | dais[i].ops = &intel_pdm_dai_ops; | ||
| 666 | } | ||
| 667 | |||
| 668 | return 0; | ||
| 669 | } | ||
| 670 | |||
| 671 | static int intel_register_dai(struct sdw_intel *sdw) | ||
| 672 | { | ||
| 673 | struct sdw_cdns *cdns = &sdw->cdns; | ||
| 674 | struct sdw_cdns_streams *stream; | ||
| 675 | struct snd_soc_dai_driver *dais; | ||
| 676 | int num_dai, ret, off = 0; | ||
| 677 | |||
| 678 | /* DAIs are created based on total number of PDIs supported */ | ||
| 679 | num_dai = cdns->pcm.num_pdi + cdns->pdm.num_pdi; | ||
| 680 | |||
| 681 | dais = devm_kcalloc(cdns->dev, num_dai, sizeof(*dais), GFP_KERNEL); | ||
| 682 | if (!dais) | ||
| 683 | return -ENOMEM; | ||
| 684 | |||
| 685 | /* Create PCM DAIs */ | ||
| 686 | stream = &cdns->pcm; | ||
| 687 | |||
| 688 | ret = intel_create_dai(cdns, dais, INTEL_PDI_IN, | ||
| 689 | stream->num_in, off, stream->num_ch_in, true); | ||
| 690 | if (ret) | ||
| 691 | return ret; | ||
| 692 | |||
| 693 | off += cdns->pcm.num_in; | ||
| 694 | ret = intel_create_dai(cdns, dais, INTEL_PDI_OUT, | ||
| 695 | cdns->pcm.num_out, off, stream->num_ch_out, true); | ||
| 696 | if (ret) | ||
| 697 | return ret; | ||
| 698 | |||
| 699 | off += cdns->pcm.num_out; | ||
| 700 | ret = intel_create_dai(cdns, dais, INTEL_PDI_BD, | ||
| 701 | cdns->pcm.num_bd, off, stream->num_ch_bd, true); | ||
| 702 | if (ret) | ||
| 703 | return ret; | ||
| 704 | |||
| 705 | /* Create PDM DAIs */ | ||
| 706 | stream = &cdns->pdm; | ||
| 707 | off += cdns->pcm.num_bd; | ||
| 708 | ret = intel_create_dai(cdns, dais, INTEL_PDI_IN, | ||
| 709 | cdns->pdm.num_in, off, stream->num_ch_in, false); | ||
| 710 | if (ret) | ||
| 711 | return ret; | ||
| 712 | |||
| 713 | off += cdns->pdm.num_in; | ||
| 714 | ret = intel_create_dai(cdns, dais, INTEL_PDI_OUT, | ||
| 715 | cdns->pdm.num_out, off, stream->num_ch_out, false); | ||
| 716 | if (ret) | ||
| 717 | return ret; | ||
| 718 | |||
| 719 | off += cdns->pdm.num_bd; | ||
| 720 | ret = intel_create_dai(cdns, dais, INTEL_PDI_BD, | ||
| 721 | cdns->pdm.num_bd, off, stream->num_ch_bd, false); | ||
| 722 | if (ret) | ||
| 723 | return ret; | ||
| 724 | |||
| 725 | return snd_soc_register_component(cdns->dev, &dai_component, | ||
| 726 | dais, num_dai); | ||
| 727 | } | ||
| 728 | |||
| 237 | static int intel_prop_read(struct sdw_bus *bus) | 729 | static int intel_prop_read(struct sdw_bus *bus) |
| 238 | { | 730 | { |
| 239 | /* Initialize with default handler to read all DisCo properties */ | 731 | /* Initialize with default handler to read all DisCo properties */ |
| @@ -252,11 +744,20 @@ static int intel_prop_read(struct sdw_bus *bus) | |||
| 252 | return 0; | 744 | return 0; |
| 253 | } | 745 | } |
| 254 | 746 | ||
| 747 | static struct sdw_master_ops sdw_intel_ops = { | ||
| 748 | .read_prop = sdw_master_read_prop, | ||
| 749 | .xfer_msg = cdns_xfer_msg, | ||
| 750 | .xfer_msg_defer = cdns_xfer_msg_defer, | ||
| 751 | .reset_page_addr = cdns_reset_page_addr, | ||
| 752 | .set_bus_conf = cdns_bus_conf, | ||
| 753 | }; | ||
| 754 | |||
| 255 | /* | 755 | /* |
| 256 | * probe and init | 756 | * probe and init |
| 257 | */ | 757 | */ |
| 258 | static int intel_probe(struct platform_device *pdev) | 758 | static int intel_probe(struct platform_device *pdev) |
| 259 | { | 759 | { |
| 760 | struct sdw_cdns_stream_config config; | ||
| 260 | struct sdw_intel *sdw; | 761 | struct sdw_intel *sdw; |
| 261 | int ret; | 762 | int ret; |
| 262 | 763 | ||
| @@ -276,8 +777,11 @@ static int intel_probe(struct platform_device *pdev) | |||
| 276 | sdw_cdns_probe(&sdw->cdns); | 777 | sdw_cdns_probe(&sdw->cdns); |
| 277 | 778 | ||
| 278 | /* Set property read ops */ | 779 | /* Set property read ops */ |
| 279 | sdw_cdns_master_ops.read_prop = intel_prop_read; | 780 | sdw_intel_ops.read_prop = intel_prop_read; |
| 280 | sdw->cdns.bus.ops = &sdw_cdns_master_ops; | 781 | sdw->cdns.bus.ops = &sdw_intel_ops; |
| 782 | |||
| 783 | sdw_intel_ops.read_prop = intel_prop_read; | ||
| 784 | sdw->cdns.bus.ops = &sdw_intel_ops; | ||
| 281 | 785 | ||
| 282 | platform_set_drvdata(pdev, sdw); | 786 | platform_set_drvdata(pdev, sdw); |
| 283 | 787 | ||
| @@ -296,9 +800,15 @@ static int intel_probe(struct platform_device *pdev) | |||
| 296 | goto err_init; | 800 | goto err_init; |
| 297 | 801 | ||
| 298 | ret = sdw_cdns_enable_interrupt(&sdw->cdns); | 802 | ret = sdw_cdns_enable_interrupt(&sdw->cdns); |
| 803 | |||
| 804 | /* Read the PDI config and initialize cadence PDI */ | ||
| 805 | intel_pdi_init(sdw, &config); | ||
| 806 | ret = sdw_cdns_pdi_init(&sdw->cdns, config); | ||
| 299 | if (ret) | 807 | if (ret) |
| 300 | goto err_init; | 808 | goto err_init; |
| 301 | 809 | ||
| 810 | intel_pdi_ch_update(sdw); | ||
| 811 | |||
| 302 | /* Acquire IRQ */ | 812 | /* Acquire IRQ */ |
| 303 | ret = request_threaded_irq(sdw->res->irq, sdw_cdns_irq, | 813 | ret = request_threaded_irq(sdw->res->irq, sdw_cdns_irq, |
| 304 | sdw_cdns_thread, IRQF_SHARED, KBUILD_MODNAME, | 814 | sdw_cdns_thread, IRQF_SHARED, KBUILD_MODNAME, |
| @@ -309,8 +819,18 @@ static int intel_probe(struct platform_device *pdev) | |||
| 309 | goto err_init; | 819 | goto err_init; |
| 310 | } | 820 | } |
| 311 | 821 | ||
| 822 | /* Register DAIs */ | ||
| 823 | ret = intel_register_dai(sdw); | ||
| 824 | if (ret) { | ||
| 825 | dev_err(sdw->cdns.dev, "DAI registration failed: %d", ret); | ||
| 826 | snd_soc_unregister_component(sdw->cdns.dev); | ||
| 827 | goto err_dai; | ||
| 828 | } | ||
| 829 | |||
| 312 | return 0; | 830 | return 0; |
| 313 | 831 | ||
| 832 | err_dai: | ||
| 833 | free_irq(sdw->res->irq, sdw); | ||
| 314 | err_init: | 834 | err_init: |
| 315 | sdw_delete_bus_master(&sdw->cdns.bus); | 835 | sdw_delete_bus_master(&sdw->cdns.bus); |
| 316 | err_master_reg: | 836 | err_master_reg: |
| @@ -324,6 +844,7 @@ static int intel_remove(struct platform_device *pdev) | |||
| 324 | sdw = platform_get_drvdata(pdev); | 844 | sdw = platform_get_drvdata(pdev); |
| 325 | 845 | ||
| 326 | free_irq(sdw->res->irq, sdw); | 846 | free_irq(sdw->res->irq, sdw); |
| 847 | snd_soc_unregister_component(sdw->cdns.dev); | ||
| 327 | sdw_delete_bus_master(&sdw->cdns.bus); | 848 | sdw_delete_bus_master(&sdw->cdns.bus); |
| 328 | 849 | ||
| 329 | return 0; | 850 | return 0; |
diff --git a/drivers/soundwire/intel.h b/drivers/soundwire/intel.h index ffa30d9535a2..c1a5bac6212e 100644 --- a/drivers/soundwire/intel.h +++ b/drivers/soundwire/intel.h | |||
| @@ -10,6 +10,8 @@ | |||
| 10 | * @shim: Audio shim pointer | 10 | * @shim: Audio shim pointer |
| 11 | * @alh: ALH (Audio Link Hub) pointer | 11 | * @alh: ALH (Audio Link Hub) pointer |
| 12 | * @irq: Interrupt line | 12 | * @irq: Interrupt line |
| 13 | * @ops: Shim callback ops | ||
| 14 | * @arg: Shim callback ops argument | ||
| 13 | * | 15 | * |
| 14 | * This is set as pdata for each link instance. | 16 | * This is set as pdata for each link instance. |
| 15 | */ | 17 | */ |
| @@ -18,6 +20,8 @@ struct sdw_intel_link_res { | |||
| 18 | void __iomem *shim; | 20 | void __iomem *shim; |
| 19 | void __iomem *alh; | 21 | void __iomem *alh; |
| 20 | int irq; | 22 | int irq; |
| 23 | const struct sdw_intel_ops *ops; | ||
| 24 | void *arg; | ||
| 21 | }; | 25 | }; |
| 22 | 26 | ||
| 23 | #endif /* __SDW_INTEL_LOCAL_H */ | 27 | #endif /* __SDW_INTEL_LOCAL_H */ |
diff --git a/drivers/soundwire/intel_init.c b/drivers/soundwire/intel_init.c index 6f2bb99526f2..d1ea6b4d0ad3 100644 --- a/drivers/soundwire/intel_init.c +++ b/drivers/soundwire/intel_init.c | |||
| @@ -111,6 +111,9 @@ static struct sdw_intel_ctx | |||
| 111 | link->res.shim = res->mmio_base + SDW_SHIM_BASE; | 111 | link->res.shim = res->mmio_base + SDW_SHIM_BASE; |
| 112 | link->res.alh = res->mmio_base + SDW_ALH_BASE; | 112 | link->res.alh = res->mmio_base + SDW_ALH_BASE; |
| 113 | 113 | ||
| 114 | link->res.ops = res->ops; | ||
| 115 | link->res.arg = res->arg; | ||
| 116 | |||
| 114 | memset(&pdevinfo, 0, sizeof(pdevinfo)); | 117 | memset(&pdevinfo, 0, sizeof(pdevinfo)); |
| 115 | 118 | ||
| 116 | pdevinfo.parent = res->parent; | 119 | pdevinfo.parent = res->parent; |
diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c new file mode 100644 index 000000000000..8974a0fcda1b --- /dev/null +++ b/drivers/soundwire/stream.c | |||
| @@ -0,0 +1,1479 @@ | |||
| 1 | // SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) | ||
| 2 | // Copyright(c) 2015-18 Intel Corporation. | ||
| 3 | |||
| 4 | /* | ||
| 5 | * stream.c - SoundWire Bus stream operations. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #include <linux/delay.h> | ||
| 9 | #include <linux/device.h> | ||
| 10 | #include <linux/init.h> | ||
| 11 | #include <linux/module.h> | ||
| 12 | #include <linux/mod_devicetable.h> | ||
| 13 | #include <linux/slab.h> | ||
| 14 | #include <linux/soundwire/sdw_registers.h> | ||
| 15 | #include <linux/soundwire/sdw.h> | ||
| 16 | #include "bus.h" | ||
| 17 | |||
| 18 | /* | ||
| 19 | * Array of supported rows and columns as per MIPI SoundWire Specification 1.1 | ||
| 20 | * | ||
| 21 | * The rows are arranged as per the array index value programmed | ||
| 22 | * in register. The index 15 has dummy value 0 in order to fill hole. | ||
| 23 | */ | ||
| 24 | int rows[SDW_FRAME_ROWS] = {48, 50, 60, 64, 75, 80, 125, 147, | ||
| 25 | 96, 100, 120, 128, 150, 160, 250, 0, | ||
| 26 | 192, 200, 240, 256, 72, 144, 90, 180}; | ||
| 27 | |||
| 28 | int cols[SDW_FRAME_COLS] = {2, 4, 6, 8, 10, 12, 14, 16}; | ||
| 29 | |||
| 30 | static int sdw_find_col_index(int col) | ||
| 31 | { | ||
| 32 | int i; | ||
| 33 | |||
| 34 | for (i = 0; i < SDW_FRAME_COLS; i++) { | ||
| 35 | if (cols[i] == col) | ||
| 36 | return i; | ||
| 37 | } | ||
| 38 | |||
| 39 | pr_warn("Requested column not found, selecting lowest column no: 2\n"); | ||
| 40 | return 0; | ||
| 41 | } | ||
| 42 | |||
| 43 | static int sdw_find_row_index(int row) | ||
| 44 | { | ||
| 45 | int i; | ||
| 46 | |||
| 47 | for (i = 0; i < SDW_FRAME_ROWS; i++) { | ||
| 48 | if (rows[i] == row) | ||
| 49 | return i; | ||
| 50 | } | ||
| 51 | |||
| 52 | pr_warn("Requested row not found, selecting lowest row no: 48\n"); | ||
| 53 | return 0; | ||
| 54 | } | ||
| 55 | static int _sdw_program_slave_port_params(struct sdw_bus *bus, | ||
| 56 | struct sdw_slave *slave, | ||
| 57 | struct sdw_transport_params *t_params, | ||
| 58 | enum sdw_dpn_type type) | ||
| 59 | { | ||
| 60 | u32 addr1, addr2, addr3, addr4; | ||
| 61 | int ret; | ||
| 62 | u16 wbuf; | ||
| 63 | |||
| 64 | if (bus->params.next_bank) { | ||
| 65 | addr1 = SDW_DPN_OFFSETCTRL2_B1(t_params->port_num); | ||
| 66 | addr2 = SDW_DPN_BLOCKCTRL3_B1(t_params->port_num); | ||
| 67 | addr3 = SDW_DPN_SAMPLECTRL2_B1(t_params->port_num); | ||
| 68 | addr4 = SDW_DPN_HCTRL_B1(t_params->port_num); | ||
| 69 | } else { | ||
| 70 | addr1 = SDW_DPN_OFFSETCTRL2_B0(t_params->port_num); | ||
| 71 | addr2 = SDW_DPN_BLOCKCTRL3_B0(t_params->port_num); | ||
| 72 | addr3 = SDW_DPN_SAMPLECTRL2_B0(t_params->port_num); | ||
| 73 | addr4 = SDW_DPN_HCTRL_B0(t_params->port_num); | ||
| 74 | } | ||
| 75 | |||
| 76 | /* Program DPN_OffsetCtrl2 registers */ | ||
| 77 | ret = sdw_write(slave, addr1, t_params->offset2); | ||
| 78 | if (ret < 0) { | ||
| 79 | dev_err(bus->dev, "DPN_OffsetCtrl2 register write failed"); | ||
| 80 | return ret; | ||
| 81 | } | ||
| 82 | |||
| 83 | /* Program DPN_BlockCtrl3 register */ | ||
| 84 | ret = sdw_write(slave, addr2, t_params->blk_pkg_mode); | ||
| 85 | if (ret < 0) { | ||
| 86 | dev_err(bus->dev, "DPN_BlockCtrl3 register write failed"); | ||
| 87 | return ret; | ||
| 88 | } | ||
| 89 | |||
| 90 | /* | ||
| 91 | * Data ports are FULL, SIMPLE and REDUCED. This function handles | ||
| 92 | * FULL and REDUCED only and and beyond this point only FULL is | ||
| 93 | * handled, so bail out if we are not FULL data port type | ||
| 94 | */ | ||
| 95 | if (type != SDW_DPN_FULL) | ||
| 96 | return ret; | ||
| 97 | |||
| 98 | /* Program DPN_SampleCtrl2 register */ | ||
| 99 | wbuf = (t_params->sample_interval - 1); | ||
| 100 | wbuf &= SDW_DPN_SAMPLECTRL_HIGH; | ||
| 101 | wbuf >>= SDW_REG_SHIFT(SDW_DPN_SAMPLECTRL_HIGH); | ||
| 102 | |||
| 103 | ret = sdw_write(slave, addr3, wbuf); | ||
| 104 | if (ret < 0) { | ||
| 105 | dev_err(bus->dev, "DPN_SampleCtrl2 register write failed"); | ||
| 106 | return ret; | ||
| 107 | } | ||
| 108 | |||
| 109 | /* Program DPN_HCtrl register */ | ||
| 110 | wbuf = t_params->hstart; | ||
| 111 | wbuf <<= SDW_REG_SHIFT(SDW_DPN_HCTRL_HSTART); | ||
| 112 | wbuf |= t_params->hstop; | ||
| 113 | |||
| 114 | ret = sdw_write(slave, addr4, wbuf); | ||
| 115 | if (ret < 0) | ||
| 116 | dev_err(bus->dev, "DPN_HCtrl register write failed"); | ||
| 117 | |||
| 118 | return ret; | ||
| 119 | } | ||
| 120 | |||
| 121 | static int sdw_program_slave_port_params(struct sdw_bus *bus, | ||
| 122 | struct sdw_slave_runtime *s_rt, | ||
| 123 | struct sdw_port_runtime *p_rt) | ||
| 124 | { | ||
| 125 | struct sdw_transport_params *t_params = &p_rt->transport_params; | ||
| 126 | struct sdw_port_params *p_params = &p_rt->port_params; | ||
| 127 | struct sdw_slave_prop *slave_prop = &s_rt->slave->prop; | ||
| 128 | u32 addr1, addr2, addr3, addr4, addr5, addr6; | ||
| 129 | struct sdw_dpn_prop *dpn_prop; | ||
| 130 | int ret; | ||
| 131 | u8 wbuf; | ||
| 132 | |||
| 133 | dpn_prop = sdw_get_slave_dpn_prop(s_rt->slave, | ||
| 134 | s_rt->direction, | ||
| 135 | t_params->port_num); | ||
| 136 | if (!dpn_prop) | ||
| 137 | return -EINVAL; | ||
| 138 | |||
| 139 | addr1 = SDW_DPN_PORTCTRL(t_params->port_num); | ||
| 140 | addr2 = SDW_DPN_BLOCKCTRL1(t_params->port_num); | ||
| 141 | |||
| 142 | if (bus->params.next_bank) { | ||
| 143 | addr3 = SDW_DPN_SAMPLECTRL1_B1(t_params->port_num); | ||
| 144 | addr4 = SDW_DPN_OFFSETCTRL1_B1(t_params->port_num); | ||
| 145 | addr5 = SDW_DPN_BLOCKCTRL2_B1(t_params->port_num); | ||
| 146 | addr6 = SDW_DPN_LANECTRL_B1(t_params->port_num); | ||
| 147 | |||
| 148 | } else { | ||
| 149 | addr3 = SDW_DPN_SAMPLECTRL1_B0(t_params->port_num); | ||
| 150 | addr4 = SDW_DPN_OFFSETCTRL1_B0(t_params->port_num); | ||
| 151 | addr5 = SDW_DPN_BLOCKCTRL2_B0(t_params->port_num); | ||
| 152 | addr6 = SDW_DPN_LANECTRL_B0(t_params->port_num); | ||
| 153 | } | ||
| 154 | |||
| 155 | /* Program DPN_PortCtrl register */ | ||
| 156 | wbuf = p_params->data_mode << SDW_REG_SHIFT(SDW_DPN_PORTCTRL_DATAMODE); | ||
| 157 | wbuf |= p_params->flow_mode; | ||
| 158 | |||
| 159 | ret = sdw_update(s_rt->slave, addr1, 0xF, wbuf); | ||
| 160 | if (ret < 0) { | ||
| 161 | dev_err(&s_rt->slave->dev, | ||
| 162 | "DPN_PortCtrl register write failed for port %d", | ||
| 163 | t_params->port_num); | ||
| 164 | return ret; | ||
| 165 | } | ||
| 166 | |||
| 167 | /* Program DPN_BlockCtrl1 register */ | ||
| 168 | ret = sdw_write(s_rt->slave, addr2, (p_params->bps - 1)); | ||
| 169 | if (ret < 0) { | ||
| 170 | dev_err(&s_rt->slave->dev, | ||
| 171 | "DPN_BlockCtrl1 register write failed for port %d", | ||
| 172 | t_params->port_num); | ||
| 173 | return ret; | ||
| 174 | } | ||
| 175 | |||
| 176 | /* Program DPN_SampleCtrl1 register */ | ||
| 177 | wbuf = (t_params->sample_interval - 1) & SDW_DPN_SAMPLECTRL_LOW; | ||
| 178 | ret = sdw_write(s_rt->slave, addr3, wbuf); | ||
| 179 | if (ret < 0) { | ||
| 180 | dev_err(&s_rt->slave->dev, | ||
| 181 | "DPN_SampleCtrl1 register write failed for port %d", | ||
| 182 | t_params->port_num); | ||
| 183 | return ret; | ||
| 184 | } | ||
| 185 | |||
| 186 | /* Program DPN_OffsetCtrl1 registers */ | ||
| 187 | ret = sdw_write(s_rt->slave, addr4, t_params->offset1); | ||
| 188 | if (ret < 0) { | ||
| 189 | dev_err(&s_rt->slave->dev, | ||
| 190 | "DPN_OffsetCtrl1 register write failed for port %d", | ||
| 191 | t_params->port_num); | ||
| 192 | return ret; | ||
| 193 | } | ||
| 194 | |||
| 195 | /* Program DPN_BlockCtrl2 register*/ | ||
| 196 | if (t_params->blk_grp_ctrl_valid) { | ||
| 197 | ret = sdw_write(s_rt->slave, addr5, t_params->blk_grp_ctrl); | ||
| 198 | if (ret < 0) { | ||
| 199 | dev_err(&s_rt->slave->dev, | ||
| 200 | "DPN_BlockCtrl2 reg write failed for port %d", | ||
| 201 | t_params->port_num); | ||
| 202 | return ret; | ||
| 203 | } | ||
| 204 | } | ||
| 205 | |||
| 206 | /* program DPN_LaneCtrl register */ | ||
| 207 | if (slave_prop->lane_control_support) { | ||
| 208 | ret = sdw_write(s_rt->slave, addr6, t_params->lane_ctrl); | ||
| 209 | if (ret < 0) { | ||
| 210 | dev_err(&s_rt->slave->dev, | ||
| 211 | "DPN_LaneCtrl register write failed for port %d", | ||
| 212 | t_params->port_num); | ||
| 213 | return ret; | ||
| 214 | } | ||
| 215 | } | ||
| 216 | |||
| 217 | if (dpn_prop->type != SDW_DPN_SIMPLE) { | ||
| 218 | ret = _sdw_program_slave_port_params(bus, s_rt->slave, | ||
| 219 | t_params, dpn_prop->type); | ||
| 220 | if (ret < 0) | ||
| 221 | dev_err(&s_rt->slave->dev, | ||
| 222 | "Transport reg write failed for port: %d", | ||
| 223 | t_params->port_num); | ||
| 224 | } | ||
| 225 | |||
| 226 | return ret; | ||
| 227 | } | ||
| 228 | |||
| 229 | static int sdw_program_master_port_params(struct sdw_bus *bus, | ||
| 230 | struct sdw_port_runtime *p_rt) | ||
| 231 | { | ||
| 232 | int ret; | ||
| 233 | |||
| 234 | /* | ||
| 235 | * we need to set transport and port parameters for the port. | ||
| 236 | * Transport parameters refers to the smaple interval, offsets and | ||
| 237 | * hstart/stop etc of the data. Port parameters refers to word | ||
| 238 | * length, flow mode etc of the port | ||
| 239 | */ | ||
| 240 | ret = bus->port_ops->dpn_set_port_transport_params(bus, | ||
| 241 | &p_rt->transport_params, | ||
| 242 | bus->params.next_bank); | ||
| 243 | if (ret < 0) | ||
| 244 | return ret; | ||
| 245 | |||
| 246 | return bus->port_ops->dpn_set_port_params(bus, | ||
| 247 | &p_rt->port_params, | ||
| 248 | bus->params.next_bank); | ||
| 249 | } | ||
| 250 | |||
| 251 | /** | ||
| 252 | * sdw_program_port_params() - Programs transport parameters of Master(s) | ||
| 253 | * and Slave(s) | ||
| 254 | * | ||
| 255 | * @m_rt: Master stream runtime | ||
| 256 | */ | ||
| 257 | static int sdw_program_port_params(struct sdw_master_runtime *m_rt) | ||
| 258 | { | ||
| 259 | struct sdw_slave_runtime *s_rt = NULL; | ||
| 260 | struct sdw_bus *bus = m_rt->bus; | ||
| 261 | struct sdw_port_runtime *p_rt; | ||
| 262 | int ret = 0; | ||
| 263 | |||
| 264 | /* Program transport & port parameters for Slave(s) */ | ||
| 265 | list_for_each_entry(s_rt, &m_rt->slave_rt_list, m_rt_node) { | ||
| 266 | list_for_each_entry(p_rt, &s_rt->port_list, port_node) { | ||
| 267 | ret = sdw_program_slave_port_params(bus, s_rt, p_rt); | ||
| 268 | if (ret < 0) | ||
| 269 | return ret; | ||
| 270 | } | ||
| 271 | } | ||
| 272 | |||
| 273 | /* Program transport & port parameters for Master(s) */ | ||
| 274 | list_for_each_entry(p_rt, &m_rt->port_list, port_node) { | ||
| 275 | ret = sdw_program_master_port_params(bus, p_rt); | ||
| 276 | if (ret < 0) | ||
| 277 | return ret; | ||
| 278 | } | ||
| 279 | |||
| 280 | return 0; | ||
| 281 | } | ||
| 282 | |||
| 283 | /** | ||
| 284 | * sdw_enable_disable_slave_ports: Enable/disable slave data port | ||
| 285 | * | ||
| 286 | * @bus: bus instance | ||
| 287 | * @s_rt: slave runtime | ||
| 288 | * @p_rt: port runtime | ||
| 289 | * @en: enable or disable operation | ||
| 290 | * | ||
| 291 | * This function only sets the enable/disable bits in the relevant bank, the | ||
| 292 | * actual enable/disable is done with a bank switch | ||
| 293 | */ | ||
| 294 | static int sdw_enable_disable_slave_ports(struct sdw_bus *bus, | ||
| 295 | struct sdw_slave_runtime *s_rt, | ||
| 296 | struct sdw_port_runtime *p_rt, bool en) | ||
| 297 | { | ||
| 298 | struct sdw_transport_params *t_params = &p_rt->transport_params; | ||
| 299 | u32 addr; | ||
| 300 | int ret; | ||
| 301 | |||
| 302 | if (bus->params.next_bank) | ||
| 303 | addr = SDW_DPN_CHANNELEN_B1(p_rt->num); | ||
| 304 | else | ||
| 305 | addr = SDW_DPN_CHANNELEN_B0(p_rt->num); | ||
| 306 | |||
| 307 | /* | ||
| 308 | * Since bus doesn't support sharing a port across two streams, | ||
| 309 | * it is safe to reset this register | ||
| 310 | */ | ||
| 311 | if (en) | ||
| 312 | ret = sdw_update(s_rt->slave, addr, 0xFF, p_rt->ch_mask); | ||
| 313 | else | ||
| 314 | ret = sdw_update(s_rt->slave, addr, 0xFF, 0x0); | ||
| 315 | |||
| 316 | if (ret < 0) | ||
| 317 | dev_err(&s_rt->slave->dev, | ||
| 318 | "Slave chn_en reg write failed:%d port:%d", | ||
| 319 | ret, t_params->port_num); | ||
| 320 | |||
| 321 | return ret; | ||
| 322 | } | ||
| 323 | |||
| 324 | static int sdw_enable_disable_master_ports(struct sdw_master_runtime *m_rt, | ||
| 325 | struct sdw_port_runtime *p_rt, bool en) | ||
| 326 | { | ||
| 327 | struct sdw_transport_params *t_params = &p_rt->transport_params; | ||
| 328 | struct sdw_bus *bus = m_rt->bus; | ||
| 329 | struct sdw_enable_ch enable_ch; | ||
| 330 | int ret = 0; | ||
| 331 | |||
| 332 | enable_ch.port_num = p_rt->num; | ||
| 333 | enable_ch.ch_mask = p_rt->ch_mask; | ||
| 334 | enable_ch.enable = en; | ||
| 335 | |||
| 336 | /* Perform Master port channel(s) enable/disable */ | ||
| 337 | if (bus->port_ops->dpn_port_enable_ch) { | ||
| 338 | ret = bus->port_ops->dpn_port_enable_ch(bus, | ||
| 339 | &enable_ch, bus->params.next_bank); | ||
| 340 | if (ret < 0) { | ||
| 341 | dev_err(bus->dev, | ||
| 342 | "Master chn_en write failed:%d port:%d", | ||
| 343 | ret, t_params->port_num); | ||
| 344 | return ret; | ||
| 345 | } | ||
| 346 | } else { | ||
| 347 | dev_err(bus->dev, | ||
| 348 | "dpn_port_enable_ch not supported, %s failed\n", | ||
| 349 | en ? "enable" : "disable"); | ||
| 350 | return -EINVAL; | ||
| 351 | } | ||
| 352 | |||
| 353 | return 0; | ||
| 354 | } | ||
| 355 | |||
| 356 | /** | ||
| 357 | * sdw_enable_disable_ports() - Enable/disable port(s) for Master and | ||
| 358 | * Slave(s) | ||
| 359 | * | ||
| 360 | * @m_rt: Master stream runtime | ||
| 361 | * @en: mode (enable/disable) | ||
| 362 | */ | ||
| 363 | static int sdw_enable_disable_ports(struct sdw_master_runtime *m_rt, bool en) | ||
| 364 | { | ||
| 365 | struct sdw_port_runtime *s_port, *m_port; | ||
| 366 | struct sdw_slave_runtime *s_rt = NULL; | ||
| 367 | int ret = 0; | ||
| 368 | |||
| 369 | /* Enable/Disable Slave port(s) */ | ||
| 370 | list_for_each_entry(s_rt, &m_rt->slave_rt_list, m_rt_node) { | ||
| 371 | list_for_each_entry(s_port, &s_rt->port_list, port_node) { | ||
| 372 | ret = sdw_enable_disable_slave_ports(m_rt->bus, s_rt, | ||
| 373 | s_port, en); | ||
| 374 | if (ret < 0) | ||
| 375 | return ret; | ||
| 376 | } | ||
| 377 | } | ||
| 378 | |||
| 379 | /* Enable/Disable Master port(s) */ | ||
| 380 | list_for_each_entry(m_port, &m_rt->port_list, port_node) { | ||
| 381 | ret = sdw_enable_disable_master_ports(m_rt, m_port, en); | ||
| 382 | if (ret < 0) | ||
| 383 | return ret; | ||
| 384 | } | ||
| 385 | |||
| 386 | return 0; | ||
| 387 | } | ||
| 388 | |||
| 389 | static int sdw_do_port_prep(struct sdw_slave_runtime *s_rt, | ||
| 390 | struct sdw_prepare_ch prep_ch, enum sdw_port_prep_ops cmd) | ||
| 391 | { | ||
| 392 | const struct sdw_slave_ops *ops = s_rt->slave->ops; | ||
| 393 | int ret; | ||
| 394 | |||
| 395 | if (ops->port_prep) { | ||
| 396 | ret = ops->port_prep(s_rt->slave, &prep_ch, cmd); | ||
| 397 | if (ret < 0) { | ||
| 398 | dev_err(&s_rt->slave->dev, | ||
| 399 | "Slave Port Prep cmd %d failed: %d", cmd, ret); | ||
| 400 | return ret; | ||
| 401 | } | ||
| 402 | } | ||
| 403 | |||
| 404 | return 0; | ||
| 405 | } | ||
| 406 | |||
| 407 | static int sdw_prep_deprep_slave_ports(struct sdw_bus *bus, | ||
| 408 | struct sdw_slave_runtime *s_rt, | ||
| 409 | struct sdw_port_runtime *p_rt, bool prep) | ||
| 410 | { | ||
| 411 | struct completion *port_ready = NULL; | ||
| 412 | struct sdw_dpn_prop *dpn_prop; | ||
| 413 | struct sdw_prepare_ch prep_ch; | ||
| 414 | unsigned int time_left; | ||
| 415 | bool intr = false; | ||
| 416 | int ret = 0, val; | ||
| 417 | u32 addr; | ||
| 418 | |||
| 419 | prep_ch.num = p_rt->num; | ||
| 420 | prep_ch.ch_mask = p_rt->ch_mask; | ||
| 421 | |||
| 422 | dpn_prop = sdw_get_slave_dpn_prop(s_rt->slave, | ||
| 423 | s_rt->direction, | ||
| 424 | prep_ch.num); | ||
| 425 | if (!dpn_prop) { | ||
| 426 | dev_err(bus->dev, | ||
| 427 | "Slave Port:%d properties not found", prep_ch.num); | ||
| 428 | return -EINVAL; | ||
| 429 | } | ||
| 430 | |||
| 431 | prep_ch.prepare = prep; | ||
| 432 | |||
| 433 | prep_ch.bank = bus->params.next_bank; | ||
| 434 | |||
| 435 | if (dpn_prop->device_interrupts || !dpn_prop->simple_ch_prep_sm) | ||
| 436 | intr = true; | ||
| 437 | |||
| 438 | /* | ||
| 439 | * Enable interrupt before Port prepare. | ||
| 440 | * For Port de-prepare, it is assumed that port | ||
| 441 | * was prepared earlier | ||
| 442 | */ | ||
| 443 | if (prep && intr) { | ||
| 444 | ret = sdw_configure_dpn_intr(s_rt->slave, p_rt->num, prep, | ||
| 445 | dpn_prop->device_interrupts); | ||
| 446 | if (ret < 0) | ||
| 447 | return ret; | ||
| 448 | } | ||
| 449 | |||
| 450 | /* Inform slave about the impending port prepare */ | ||
| 451 | sdw_do_port_prep(s_rt, prep_ch, SDW_OPS_PORT_PRE_PREP); | ||
| 452 | |||
| 453 | /* Prepare Slave port implementing CP_SM */ | ||
| 454 | if (!dpn_prop->simple_ch_prep_sm) { | ||
| 455 | addr = SDW_DPN_PREPARECTRL(p_rt->num); | ||
| 456 | |||
| 457 | if (prep) | ||
| 458 | ret = sdw_update(s_rt->slave, addr, | ||
| 459 | 0xFF, p_rt->ch_mask); | ||
| 460 | else | ||
| 461 | ret = sdw_update(s_rt->slave, addr, 0xFF, 0x0); | ||
| 462 | |||
| 463 | if (ret < 0) { | ||
| 464 | dev_err(&s_rt->slave->dev, | ||
| 465 | "Slave prep_ctrl reg write failed"); | ||
| 466 | return ret; | ||
| 467 | } | ||
| 468 | |||
| 469 | /* Wait for completion on port ready */ | ||
| 470 | port_ready = &s_rt->slave->port_ready[prep_ch.num]; | ||
| 471 | time_left = wait_for_completion_timeout(port_ready, | ||
| 472 | msecs_to_jiffies(dpn_prop->ch_prep_timeout)); | ||
| 473 | |||
| 474 | val = sdw_read(s_rt->slave, SDW_DPN_PREPARESTATUS(p_rt->num)); | ||
| 475 | val &= p_rt->ch_mask; | ||
| 476 | if (!time_left || val) { | ||
| 477 | dev_err(&s_rt->slave->dev, | ||
| 478 | "Chn prep failed for port:%d", prep_ch.num); | ||
| 479 | return -ETIMEDOUT; | ||
| 480 | } | ||
| 481 | } | ||
| 482 | |||
| 483 | /* Inform slaves about ports prepared */ | ||
| 484 | sdw_do_port_prep(s_rt, prep_ch, SDW_OPS_PORT_POST_PREP); | ||
| 485 | |||
| 486 | /* Disable interrupt after Port de-prepare */ | ||
| 487 | if (!prep && intr) | ||
| 488 | ret = sdw_configure_dpn_intr(s_rt->slave, p_rt->num, prep, | ||
| 489 | dpn_prop->device_interrupts); | ||
| 490 | |||
| 491 | return ret; | ||
| 492 | } | ||
| 493 | |||
| 494 | static int sdw_prep_deprep_master_ports(struct sdw_master_runtime *m_rt, | ||
| 495 | struct sdw_port_runtime *p_rt, bool prep) | ||
| 496 | { | ||
| 497 | struct sdw_transport_params *t_params = &p_rt->transport_params; | ||
| 498 | struct sdw_bus *bus = m_rt->bus; | ||
| 499 | const struct sdw_master_port_ops *ops = bus->port_ops; | ||
| 500 | struct sdw_prepare_ch prep_ch; | ||
| 501 | int ret = 0; | ||
| 502 | |||
| 503 | prep_ch.num = p_rt->num; | ||
| 504 | prep_ch.ch_mask = p_rt->ch_mask; | ||
| 505 | prep_ch.prepare = prep; /* Prepare/De-prepare */ | ||
| 506 | prep_ch.bank = bus->params.next_bank; | ||
| 507 | |||
| 508 | /* Pre-prepare/Pre-deprepare port(s) */ | ||
| 509 | if (ops->dpn_port_prep) { | ||
| 510 | ret = ops->dpn_port_prep(bus, &prep_ch); | ||
| 511 | if (ret < 0) { | ||
| 512 | dev_err(bus->dev, "Port prepare failed for port:%d", | ||
| 513 | t_params->port_num); | ||
| 514 | return ret; | ||
| 515 | } | ||
| 516 | } | ||
| 517 | |||
| 518 | return ret; | ||
| 519 | } | ||
| 520 | |||
| 521 | /** | ||
| 522 | * sdw_prep_deprep_ports() - Prepare/De-prepare port(s) for Master(s) and | ||
| 523 | * Slave(s) | ||
| 524 | * | ||
| 525 | * @m_rt: Master runtime handle | ||
| 526 | * @prep: Prepare or De-prepare | ||
| 527 | */ | ||
| 528 | static int sdw_prep_deprep_ports(struct sdw_master_runtime *m_rt, bool prep) | ||
| 529 | { | ||
| 530 | struct sdw_slave_runtime *s_rt = NULL; | ||
| 531 | struct sdw_port_runtime *p_rt; | ||
| 532 | int ret = 0; | ||
| 533 | |||
| 534 | /* Prepare/De-prepare Slave port(s) */ | ||
| 535 | list_for_each_entry(s_rt, &m_rt->slave_rt_list, m_rt_node) { | ||
| 536 | list_for_each_entry(p_rt, &s_rt->port_list, port_node) { | ||
| 537 | ret = sdw_prep_deprep_slave_ports(m_rt->bus, s_rt, | ||
| 538 | p_rt, prep); | ||
| 539 | if (ret < 0) | ||
| 540 | return ret; | ||
| 541 | } | ||
| 542 | } | ||
| 543 | |||
| 544 | /* Prepare/De-prepare Master port(s) */ | ||
| 545 | list_for_each_entry(p_rt, &m_rt->port_list, port_node) { | ||
| 546 | ret = sdw_prep_deprep_master_ports(m_rt, p_rt, prep); | ||
| 547 | if (ret < 0) | ||
| 548 | return ret; | ||
| 549 | } | ||
| 550 | |||
| 551 | return ret; | ||
| 552 | } | ||
| 553 | |||
| 554 | /** | ||
| 555 | * sdw_notify_config() - Notify bus configuration | ||
| 556 | * | ||
| 557 | * @m_rt: Master runtime handle | ||
| 558 | * | ||
| 559 | * This function notifies the Master(s) and Slave(s) of the | ||
| 560 | * new bus configuration. | ||
| 561 | */ | ||
| 562 | static int sdw_notify_config(struct sdw_master_runtime *m_rt) | ||
| 563 | { | ||
| 564 | struct sdw_slave_runtime *s_rt; | ||
| 565 | struct sdw_bus *bus = m_rt->bus; | ||
| 566 | struct sdw_slave *slave; | ||
| 567 | int ret = 0; | ||
| 568 | |||
| 569 | if (bus->ops->set_bus_conf) { | ||
| 570 | ret = bus->ops->set_bus_conf(bus, &bus->params); | ||
| 571 | if (ret < 0) | ||
| 572 | return ret; | ||
| 573 | } | ||
| 574 | |||
| 575 | list_for_each_entry(s_rt, &m_rt->slave_rt_list, m_rt_node) { | ||
| 576 | slave = s_rt->slave; | ||
| 577 | |||
| 578 | if (slave->ops->bus_config) { | ||
| 579 | ret = slave->ops->bus_config(slave, &bus->params); | ||
| 580 | if (ret < 0) | ||
| 581 | dev_err(bus->dev, "Notify Slave: %d failed", | ||
| 582 | slave->dev_num); | ||
| 583 | return ret; | ||
| 584 | } | ||
| 585 | } | ||
| 586 | |||
| 587 | return ret; | ||
| 588 | } | ||
| 589 | |||
| 590 | /** | ||
| 591 | * sdw_program_params() - Program transport and port parameters for Master(s) | ||
| 592 | * and Slave(s) | ||
| 593 | * | ||
| 594 | * @bus: SDW bus instance | ||
| 595 | */ | ||
| 596 | static int sdw_program_params(struct sdw_bus *bus) | ||
| 597 | { | ||
| 598 | struct sdw_master_runtime *m_rt = NULL; | ||
| 599 | int ret = 0; | ||
| 600 | |||
| 601 | list_for_each_entry(m_rt, &bus->m_rt_list, bus_node) { | ||
| 602 | ret = sdw_program_port_params(m_rt); | ||
| 603 | if (ret < 0) { | ||
| 604 | dev_err(bus->dev, | ||
| 605 | "Program transport params failed: %d", ret); | ||
| 606 | return ret; | ||
| 607 | } | ||
| 608 | |||
| 609 | ret = sdw_notify_config(m_rt); | ||
| 610 | if (ret < 0) { | ||
| 611 | dev_err(bus->dev, "Notify bus config failed: %d", ret); | ||
| 612 | return ret; | ||
| 613 | } | ||
| 614 | |||
| 615 | /* Enable port(s) on alternate bank for all active streams */ | ||
| 616 | if (m_rt->stream->state != SDW_STREAM_ENABLED) | ||
| 617 | continue; | ||
| 618 | |||
| 619 | ret = sdw_enable_disable_ports(m_rt, true); | ||
| 620 | if (ret < 0) { | ||
| 621 | dev_err(bus->dev, "Enable channel failed: %d", ret); | ||
| 622 | return ret; | ||
| 623 | } | ||
| 624 | } | ||
| 625 | |||
| 626 | return ret; | ||
| 627 | } | ||
| 628 | |||
| 629 | static int sdw_bank_switch(struct sdw_bus *bus) | ||
| 630 | { | ||
| 631 | int col_index, row_index; | ||
| 632 | struct sdw_msg *wr_msg; | ||
| 633 | u8 *wbuf = NULL; | ||
| 634 | int ret = 0; | ||
| 635 | u16 addr; | ||
| 636 | |||
| 637 | wr_msg = kzalloc(sizeof(*wr_msg), GFP_KERNEL); | ||
| 638 | if (!wr_msg) | ||
| 639 | return -ENOMEM; | ||
| 640 | |||
| 641 | wbuf = kzalloc(sizeof(*wbuf), GFP_KERNEL); | ||
| 642 | if (!wbuf) { | ||
| 643 | ret = -ENOMEM; | ||
| 644 | goto error_1; | ||
| 645 | } | ||
| 646 | |||
| 647 | /* Get row and column index to program register */ | ||
| 648 | col_index = sdw_find_col_index(bus->params.col); | ||
| 649 | row_index = sdw_find_row_index(bus->params.row); | ||
| 650 | wbuf[0] = col_index | (row_index << 3); | ||
| 651 | |||
| 652 | if (bus->params.next_bank) | ||
| 653 | addr = SDW_SCP_FRAMECTRL_B1; | ||
| 654 | else | ||
| 655 | addr = SDW_SCP_FRAMECTRL_B0; | ||
| 656 | |||
| 657 | sdw_fill_msg(wr_msg, NULL, addr, 1, SDW_BROADCAST_DEV_NUM, | ||
| 658 | SDW_MSG_FLAG_WRITE, wbuf); | ||
| 659 | wr_msg->ssp_sync = true; | ||
| 660 | |||
| 661 | ret = sdw_transfer(bus, wr_msg); | ||
| 662 | if (ret < 0) { | ||
| 663 | dev_err(bus->dev, "Slave frame_ctrl reg write failed"); | ||
| 664 | goto error; | ||
| 665 | } | ||
| 666 | |||
| 667 | kfree(wr_msg); | ||
| 668 | kfree(wbuf); | ||
| 669 | bus->defer_msg.msg = NULL; | ||
| 670 | bus->params.curr_bank = !bus->params.curr_bank; | ||
| 671 | bus->params.next_bank = !bus->params.next_bank; | ||
| 672 | |||
| 673 | return 0; | ||
| 674 | |||
| 675 | error: | ||
| 676 | kfree(wbuf); | ||
| 677 | error_1: | ||
| 678 | kfree(wr_msg); | ||
| 679 | return ret; | ||
| 680 | } | ||
| 681 | |||
| 682 | static int do_bank_switch(struct sdw_stream_runtime *stream) | ||
| 683 | { | ||
| 684 | struct sdw_master_runtime *m_rt = stream->m_rt; | ||
| 685 | const struct sdw_master_ops *ops; | ||
| 686 | struct sdw_bus *bus = m_rt->bus; | ||
| 687 | int ret = 0; | ||
| 688 | |||
| 689 | ops = bus->ops; | ||
| 690 | |||
| 691 | /* Pre-bank switch */ | ||
| 692 | if (ops->pre_bank_switch) { | ||
| 693 | ret = ops->pre_bank_switch(bus); | ||
| 694 | if (ret < 0) { | ||
| 695 | dev_err(bus->dev, "Pre bank switch op failed: %d", ret); | ||
| 696 | return ret; | ||
| 697 | } | ||
| 698 | } | ||
| 699 | |||
| 700 | /* Bank switch */ | ||
| 701 | ret = sdw_bank_switch(bus); | ||
| 702 | if (ret < 0) { | ||
| 703 | dev_err(bus->dev, "Bank switch failed: %d", ret); | ||
| 704 | return ret; | ||
| 705 | } | ||
| 706 | |||
| 707 | /* Post-bank switch */ | ||
| 708 | if (ops->post_bank_switch) { | ||
| 709 | ret = ops->post_bank_switch(bus); | ||
| 710 | if (ret < 0) { | ||
| 711 | dev_err(bus->dev, | ||
| 712 | "Post bank switch op failed: %d", ret); | ||
| 713 | } | ||
| 714 | } | ||
| 715 | |||
| 716 | return ret; | ||
| 717 | } | ||
| 718 | |||
| 719 | /** | ||
| 720 | * sdw_release_stream() - Free the assigned stream runtime | ||
| 721 | * | ||
| 722 | * @stream: SoundWire stream runtime | ||
| 723 | * | ||
| 724 | * sdw_release_stream should be called only once per stream | ||
| 725 | */ | ||
| 726 | void sdw_release_stream(struct sdw_stream_runtime *stream) | ||
| 727 | { | ||
| 728 | kfree(stream); | ||
| 729 | } | ||
| 730 | EXPORT_SYMBOL(sdw_release_stream); | ||
| 731 | |||
| 732 | /** | ||
| 733 | * sdw_alloc_stream() - Allocate and return stream runtime | ||
| 734 | * | ||
| 735 | * @stream_name: SoundWire stream name | ||
| 736 | * | ||
| 737 | * Allocates a SoundWire stream runtime instance. | ||
| 738 | * sdw_alloc_stream should be called only once per stream. Typically | ||
| 739 | * invoked from ALSA/ASoC machine/platform driver. | ||
| 740 | */ | ||
| 741 | struct sdw_stream_runtime *sdw_alloc_stream(char *stream_name) | ||
| 742 | { | ||
| 743 | struct sdw_stream_runtime *stream; | ||
| 744 | |||
| 745 | stream = kzalloc(sizeof(*stream), GFP_KERNEL); | ||
| 746 | if (!stream) | ||
| 747 | return NULL; | ||
| 748 | |||
| 749 | stream->name = stream_name; | ||
| 750 | stream->state = SDW_STREAM_ALLOCATED; | ||
| 751 | |||
| 752 | return stream; | ||
| 753 | } | ||
| 754 | EXPORT_SYMBOL(sdw_alloc_stream); | ||
| 755 | |||
| 756 | /** | ||
| 757 | * sdw_alloc_master_rt() - Allocates and initialize Master runtime handle | ||
| 758 | * | ||
| 759 | * @bus: SDW bus instance | ||
| 760 | * @stream_config: Stream configuration | ||
| 761 | * @stream: Stream runtime handle. | ||
| 762 | * | ||
| 763 | * This function is to be called with bus_lock held. | ||
| 764 | */ | ||
| 765 | static struct sdw_master_runtime | ||
| 766 | *sdw_alloc_master_rt(struct sdw_bus *bus, | ||
| 767 | struct sdw_stream_config *stream_config, | ||
| 768 | struct sdw_stream_runtime *stream) | ||
| 769 | { | ||
| 770 | struct sdw_master_runtime *m_rt; | ||
| 771 | |||
| 772 | m_rt = stream->m_rt; | ||
| 773 | |||
| 774 | /* | ||
| 775 | * check if Master is already allocated (as a result of Slave adding | ||
| 776 | * it first), if so skip allocation and go to configure | ||
| 777 | */ | ||
| 778 | if (m_rt) | ||
| 779 | goto stream_config; | ||
| 780 | |||
| 781 | m_rt = kzalloc(sizeof(*m_rt), GFP_KERNEL); | ||
| 782 | if (!m_rt) | ||
| 783 | return NULL; | ||
| 784 | |||
| 785 | /* Initialization of Master runtime handle */ | ||
| 786 | INIT_LIST_HEAD(&m_rt->port_list); | ||
| 787 | INIT_LIST_HEAD(&m_rt->slave_rt_list); | ||
| 788 | stream->m_rt = m_rt; | ||
| 789 | |||
| 790 | list_add_tail(&m_rt->bus_node, &bus->m_rt_list); | ||
| 791 | |||
| 792 | stream_config: | ||
| 793 | m_rt->ch_count = stream_config->ch_count; | ||
| 794 | m_rt->bus = bus; | ||
| 795 | m_rt->stream = stream; | ||
| 796 | m_rt->direction = stream_config->direction; | ||
| 797 | |||
| 798 | return m_rt; | ||
| 799 | } | ||
| 800 | |||
| 801 | /** | ||
| 802 | * sdw_alloc_slave_rt() - Allocate and initialize Slave runtime handle. | ||
| 803 | * | ||
| 804 | * @slave: Slave handle | ||
| 805 | * @stream_config: Stream configuration | ||
| 806 | * @stream: Stream runtime handle | ||
| 807 | * | ||
| 808 | * This function is to be called with bus_lock held. | ||
| 809 | */ | ||
| 810 | static struct sdw_slave_runtime | ||
| 811 | *sdw_alloc_slave_rt(struct sdw_slave *slave, | ||
| 812 | struct sdw_stream_config *stream_config, | ||
| 813 | struct sdw_stream_runtime *stream) | ||
| 814 | { | ||
| 815 | struct sdw_slave_runtime *s_rt = NULL; | ||
| 816 | |||
| 817 | s_rt = kzalloc(sizeof(*s_rt), GFP_KERNEL); | ||
| 818 | if (!s_rt) | ||
| 819 | return NULL; | ||
| 820 | |||
| 821 | INIT_LIST_HEAD(&s_rt->port_list); | ||
| 822 | s_rt->ch_count = stream_config->ch_count; | ||
| 823 | s_rt->direction = stream_config->direction; | ||
| 824 | s_rt->slave = slave; | ||
| 825 | |||
| 826 | return s_rt; | ||
| 827 | } | ||
| 828 | |||
| 829 | static void sdw_master_port_release(struct sdw_bus *bus, | ||
| 830 | struct sdw_master_runtime *m_rt) | ||
| 831 | { | ||
| 832 | struct sdw_port_runtime *p_rt, *_p_rt; | ||
| 833 | |||
| 834 | list_for_each_entry_safe(p_rt, _p_rt, | ||
| 835 | &m_rt->port_list, port_node) { | ||
| 836 | list_del(&p_rt->port_node); | ||
| 837 | kfree(p_rt); | ||
| 838 | } | ||
| 839 | } | ||
| 840 | |||
| 841 | static void sdw_slave_port_release(struct sdw_bus *bus, | ||
| 842 | struct sdw_slave *slave, | ||
| 843 | struct sdw_stream_runtime *stream) | ||
| 844 | { | ||
| 845 | struct sdw_port_runtime *p_rt, *_p_rt; | ||
| 846 | struct sdw_master_runtime *m_rt = stream->m_rt; | ||
| 847 | struct sdw_slave_runtime *s_rt; | ||
| 848 | |||
| 849 | list_for_each_entry(s_rt, &m_rt->slave_rt_list, m_rt_node) { | ||
| 850 | if (s_rt->slave != slave) | ||
| 851 | continue; | ||
| 852 | |||
| 853 | list_for_each_entry_safe(p_rt, _p_rt, | ||
| 854 | &s_rt->port_list, port_node) { | ||
| 855 | list_del(&p_rt->port_node); | ||
| 856 | kfree(p_rt); | ||
| 857 | } | ||
| 858 | } | ||
| 859 | } | ||
| 860 | |||
| 861 | /** | ||
| 862 | * sdw_release_slave_stream() - Free Slave(s) runtime handle | ||
| 863 | * | ||
| 864 | * @slave: Slave handle. | ||
| 865 | * @stream: Stream runtime handle. | ||
| 866 | * | ||
| 867 | * This function is to be called with bus_lock held. | ||
| 868 | */ | ||
| 869 | static void sdw_release_slave_stream(struct sdw_slave *slave, | ||
| 870 | struct sdw_stream_runtime *stream) | ||
| 871 | { | ||
| 872 | struct sdw_slave_runtime *s_rt, *_s_rt; | ||
| 873 | struct sdw_master_runtime *m_rt = stream->m_rt; | ||
| 874 | |||
| 875 | /* Retrieve Slave runtime handle */ | ||
| 876 | list_for_each_entry_safe(s_rt, _s_rt, | ||
| 877 | &m_rt->slave_rt_list, m_rt_node) { | ||
| 878 | |||
| 879 | if (s_rt->slave == slave) { | ||
| 880 | list_del(&s_rt->m_rt_node); | ||
| 881 | kfree(s_rt); | ||
| 882 | return; | ||
| 883 | } | ||
| 884 | } | ||
| 885 | } | ||
| 886 | |||
| 887 | /** | ||
| 888 | * sdw_release_master_stream() - Free Master runtime handle | ||
| 889 | * | ||
| 890 | * @stream: Stream runtime handle. | ||
| 891 | * | ||
| 892 | * This function is to be called with bus_lock held | ||
| 893 | * It frees the Master runtime handle and associated Slave(s) runtime | ||
| 894 | * handle. If this is called first then sdw_release_slave_stream() will have | ||
| 895 | * no effect as Slave(s) runtime handle would already be freed up. | ||
| 896 | */ | ||
| 897 | static void sdw_release_master_stream(struct sdw_stream_runtime *stream) | ||
| 898 | { | ||
| 899 | struct sdw_master_runtime *m_rt = stream->m_rt; | ||
| 900 | struct sdw_slave_runtime *s_rt, *_s_rt; | ||
| 901 | |||
| 902 | list_for_each_entry_safe(s_rt, _s_rt, | ||
| 903 | &m_rt->slave_rt_list, m_rt_node) | ||
| 904 | sdw_stream_remove_slave(s_rt->slave, stream); | ||
| 905 | |||
| 906 | list_del(&m_rt->bus_node); | ||
| 907 | } | ||
| 908 | |||
| 909 | /** | ||
| 910 | * sdw_stream_remove_master() - Remove master from sdw_stream | ||
| 911 | * | ||
| 912 | * @bus: SDW Bus instance | ||
| 913 | * @stream: SoundWire stream | ||
| 914 | * | ||
| 915 | * This removes and frees port_rt and master_rt from a stream | ||
| 916 | */ | ||
| 917 | int sdw_stream_remove_master(struct sdw_bus *bus, | ||
| 918 | struct sdw_stream_runtime *stream) | ||
| 919 | { | ||
| 920 | mutex_lock(&bus->bus_lock); | ||
| 921 | |||
| 922 | sdw_release_master_stream(stream); | ||
| 923 | sdw_master_port_release(bus, stream->m_rt); | ||
| 924 | stream->state = SDW_STREAM_RELEASED; | ||
| 925 | kfree(stream->m_rt); | ||
| 926 | stream->m_rt = NULL; | ||
| 927 | |||
| 928 | mutex_unlock(&bus->bus_lock); | ||
| 929 | |||
| 930 | return 0; | ||
| 931 | } | ||
| 932 | EXPORT_SYMBOL(sdw_stream_remove_master); | ||
| 933 | |||
| 934 | /** | ||
| 935 | * sdw_stream_remove_slave() - Remove slave from sdw_stream | ||
| 936 | * | ||
| 937 | * @slave: SDW Slave instance | ||
| 938 | * @stream: SoundWire stream | ||
| 939 | * | ||
| 940 | * This removes and frees port_rt and slave_rt from a stream | ||
| 941 | */ | ||
| 942 | int sdw_stream_remove_slave(struct sdw_slave *slave, | ||
| 943 | struct sdw_stream_runtime *stream) | ||
| 944 | { | ||
| 945 | mutex_lock(&slave->bus->bus_lock); | ||
| 946 | |||
| 947 | sdw_slave_port_release(slave->bus, slave, stream); | ||
| 948 | sdw_release_slave_stream(slave, stream); | ||
| 949 | |||
| 950 | mutex_unlock(&slave->bus->bus_lock); | ||
| 951 | |||
| 952 | return 0; | ||
| 953 | } | ||
| 954 | EXPORT_SYMBOL(sdw_stream_remove_slave); | ||
| 955 | |||
| 956 | /** | ||
| 957 | * sdw_config_stream() - Configure the allocated stream | ||
| 958 | * | ||
| 959 | * @dev: SDW device | ||
| 960 | * @stream: SoundWire stream | ||
| 961 | * @stream_config: Stream configuration for audio stream | ||
| 962 | * @is_slave: is API called from Slave or Master | ||
| 963 | * | ||
| 964 | * This function is to be called with bus_lock held. | ||
| 965 | */ | ||
| 966 | static int sdw_config_stream(struct device *dev, | ||
| 967 | struct sdw_stream_runtime *stream, | ||
| 968 | struct sdw_stream_config *stream_config, bool is_slave) | ||
| 969 | { | ||
| 970 | /* | ||
| 971 | * Update the stream rate, channel and bps based on data | ||
| 972 | * source. For more than one data source (multilink), | ||
| 973 | * match the rate, bps, stream type and increment number of channels. | ||
| 974 | * | ||
| 975 | * If rate/bps is zero, it means the values are not set, so skip | ||
| 976 | * comparison and allow the value to be set and stored in stream | ||
| 977 | */ | ||
| 978 | if (stream->params.rate && | ||
| 979 | stream->params.rate != stream_config->frame_rate) { | ||
| 980 | dev_err(dev, "rate not matching, stream:%s", stream->name); | ||
| 981 | return -EINVAL; | ||
| 982 | } | ||
| 983 | |||
| 984 | if (stream->params.bps && | ||
| 985 | stream->params.bps != stream_config->bps) { | ||
| 986 | dev_err(dev, "bps not matching, stream:%s", stream->name); | ||
| 987 | return -EINVAL; | ||
| 988 | } | ||
| 989 | |||
| 990 | stream->type = stream_config->type; | ||
| 991 | stream->params.rate = stream_config->frame_rate; | ||
| 992 | stream->params.bps = stream_config->bps; | ||
| 993 | |||
| 994 | /* TODO: Update this check during Device-device support */ | ||
| 995 | if (is_slave) | ||
| 996 | stream->params.ch_count += stream_config->ch_count; | ||
| 997 | |||
| 998 | return 0; | ||
| 999 | } | ||
| 1000 | |||
| 1001 | static int sdw_is_valid_port_range(struct device *dev, | ||
| 1002 | struct sdw_port_runtime *p_rt) | ||
| 1003 | { | ||
| 1004 | if (!SDW_VALID_PORT_RANGE(p_rt->num)) { | ||
| 1005 | dev_err(dev, | ||
| 1006 | "SoundWire: Invalid port number :%d", p_rt->num); | ||
| 1007 | return -EINVAL; | ||
| 1008 | } | ||
| 1009 | |||
| 1010 | return 0; | ||
| 1011 | } | ||
| 1012 | |||
| 1013 | static struct sdw_port_runtime *sdw_port_alloc(struct device *dev, | ||
| 1014 | struct sdw_port_config *port_config, | ||
| 1015 | int port_index) | ||
| 1016 | { | ||
| 1017 | struct sdw_port_runtime *p_rt; | ||
| 1018 | |||
| 1019 | p_rt = kzalloc(sizeof(*p_rt), GFP_KERNEL); | ||
| 1020 | if (!p_rt) | ||
| 1021 | return NULL; | ||
| 1022 | |||
| 1023 | p_rt->ch_mask = port_config[port_index].ch_mask; | ||
| 1024 | p_rt->num = port_config[port_index].num; | ||
| 1025 | |||
| 1026 | return p_rt; | ||
| 1027 | } | ||
| 1028 | |||
| 1029 | static int sdw_master_port_config(struct sdw_bus *bus, | ||
| 1030 | struct sdw_master_runtime *m_rt, | ||
| 1031 | struct sdw_port_config *port_config, | ||
| 1032 | unsigned int num_ports) | ||
| 1033 | { | ||
| 1034 | struct sdw_port_runtime *p_rt; | ||
| 1035 | int i; | ||
| 1036 | |||
| 1037 | /* Iterate for number of ports to perform initialization */ | ||
| 1038 | for (i = 0; i < num_ports; i++) { | ||
| 1039 | p_rt = sdw_port_alloc(bus->dev, port_config, i); | ||
| 1040 | if (!p_rt) | ||
| 1041 | return -ENOMEM; | ||
| 1042 | |||
| 1043 | /* | ||
| 1044 | * TODO: Check port capabilities for requested | ||
| 1045 | * configuration (audio mode support) | ||
| 1046 | */ | ||
| 1047 | |||
| 1048 | list_add_tail(&p_rt->port_node, &m_rt->port_list); | ||
| 1049 | } | ||
| 1050 | |||
| 1051 | return 0; | ||
| 1052 | } | ||
| 1053 | |||
| 1054 | static int sdw_slave_port_config(struct sdw_slave *slave, | ||
| 1055 | struct sdw_slave_runtime *s_rt, | ||
| 1056 | struct sdw_port_config *port_config, | ||
| 1057 | unsigned int num_config) | ||
| 1058 | { | ||
| 1059 | struct sdw_port_runtime *p_rt; | ||
| 1060 | int i, ret; | ||
| 1061 | |||
| 1062 | /* Iterate for number of ports to perform initialization */ | ||
| 1063 | for (i = 0; i < num_config; i++) { | ||
| 1064 | p_rt = sdw_port_alloc(&slave->dev, port_config, i); | ||
| 1065 | if (!p_rt) | ||
| 1066 | return -ENOMEM; | ||
| 1067 | |||
| 1068 | /* | ||
| 1069 | * TODO: Check valid port range as defined by DisCo/ | ||
| 1070 | * slave | ||
| 1071 | */ | ||
| 1072 | ret = sdw_is_valid_port_range(&slave->dev, p_rt); | ||
| 1073 | if (ret < 0) { | ||
| 1074 | kfree(p_rt); | ||
| 1075 | return ret; | ||
| 1076 | } | ||
| 1077 | |||
| 1078 | /* | ||
| 1079 | * TODO: Check port capabilities for requested | ||
| 1080 | * configuration (audio mode support) | ||
| 1081 | */ | ||
| 1082 | |||
| 1083 | list_add_tail(&p_rt->port_node, &s_rt->port_list); | ||
| 1084 | } | ||
| 1085 | |||
| 1086 | return 0; | ||
| 1087 | } | ||
| 1088 | |||
| 1089 | /** | ||
| 1090 | * sdw_stream_add_master() - Allocate and add master runtime to a stream | ||
| 1091 | * | ||
| 1092 | * @bus: SDW Bus instance | ||
| 1093 | * @stream_config: Stream configuration for audio stream | ||
| 1094 | * @port_config: Port configuration for audio stream | ||
| 1095 | * @num_ports: Number of ports | ||
| 1096 | * @stream: SoundWire stream | ||
| 1097 | */ | ||
| 1098 | int sdw_stream_add_master(struct sdw_bus *bus, | ||
| 1099 | struct sdw_stream_config *stream_config, | ||
| 1100 | struct sdw_port_config *port_config, | ||
| 1101 | unsigned int num_ports, | ||
| 1102 | struct sdw_stream_runtime *stream) | ||
| 1103 | { | ||
| 1104 | struct sdw_master_runtime *m_rt = NULL; | ||
| 1105 | int ret; | ||
| 1106 | |||
| 1107 | mutex_lock(&bus->bus_lock); | ||
| 1108 | |||
| 1109 | m_rt = sdw_alloc_master_rt(bus, stream_config, stream); | ||
| 1110 | if (!m_rt) { | ||
| 1111 | dev_err(bus->dev, | ||
| 1112 | "Master runtime config failed for stream:%s", | ||
| 1113 | stream->name); | ||
| 1114 | ret = -ENOMEM; | ||
| 1115 | goto error; | ||
| 1116 | } | ||
| 1117 | |||
| 1118 | ret = sdw_config_stream(bus->dev, stream, stream_config, false); | ||
| 1119 | if (ret) | ||
| 1120 | goto stream_error; | ||
| 1121 | |||
| 1122 | ret = sdw_master_port_config(bus, m_rt, port_config, num_ports); | ||
| 1123 | if (ret) | ||
| 1124 | goto stream_error; | ||
| 1125 | |||
| 1126 | stream->state = SDW_STREAM_CONFIGURED; | ||
| 1127 | |||
| 1128 | stream_error: | ||
| 1129 | sdw_release_master_stream(stream); | ||
| 1130 | error: | ||
| 1131 | mutex_unlock(&bus->bus_lock); | ||
| 1132 | return ret; | ||
| 1133 | } | ||
| 1134 | EXPORT_SYMBOL(sdw_stream_add_master); | ||
| 1135 | |||
| 1136 | /** | ||
| 1137 | * sdw_stream_add_slave() - Allocate and add master/slave runtime to a stream | ||
| 1138 | * | ||
| 1139 | * @slave: SDW Slave instance | ||
| 1140 | * @stream_config: Stream configuration for audio stream | ||
| 1141 | * @stream: SoundWire stream | ||
| 1142 | * @port_config: Port configuration for audio stream | ||
| 1143 | * @num_ports: Number of ports | ||
| 1144 | */ | ||
| 1145 | int sdw_stream_add_slave(struct sdw_slave *slave, | ||
| 1146 | struct sdw_stream_config *stream_config, | ||
| 1147 | struct sdw_port_config *port_config, | ||
| 1148 | unsigned int num_ports, | ||
| 1149 | struct sdw_stream_runtime *stream) | ||
| 1150 | { | ||
| 1151 | struct sdw_slave_runtime *s_rt; | ||
| 1152 | struct sdw_master_runtime *m_rt; | ||
| 1153 | int ret; | ||
| 1154 | |||
| 1155 | mutex_lock(&slave->bus->bus_lock); | ||
| 1156 | |||
| 1157 | /* | ||
| 1158 | * If this API is invoked by Slave first then m_rt is not valid. | ||
| 1159 | * So, allocate m_rt and add Slave to it. | ||
| 1160 | */ | ||
| 1161 | m_rt = sdw_alloc_master_rt(slave->bus, stream_config, stream); | ||
| 1162 | if (!m_rt) { | ||
| 1163 | dev_err(&slave->dev, | ||
| 1164 | "alloc master runtime failed for stream:%s", | ||
| 1165 | stream->name); | ||
| 1166 | ret = -ENOMEM; | ||
| 1167 | goto error; | ||
| 1168 | } | ||
| 1169 | |||
| 1170 | s_rt = sdw_alloc_slave_rt(slave, stream_config, stream); | ||
| 1171 | if (!s_rt) { | ||
| 1172 | dev_err(&slave->dev, | ||
| 1173 | "Slave runtime config failed for stream:%s", | ||
| 1174 | stream->name); | ||
| 1175 | ret = -ENOMEM; | ||
| 1176 | goto stream_error; | ||
| 1177 | } | ||
| 1178 | |||
| 1179 | ret = sdw_config_stream(&slave->dev, stream, stream_config, true); | ||
| 1180 | if (ret) | ||
| 1181 | goto stream_error; | ||
| 1182 | |||
| 1183 | list_add_tail(&s_rt->m_rt_node, &m_rt->slave_rt_list); | ||
| 1184 | |||
| 1185 | ret = sdw_slave_port_config(slave, s_rt, port_config, num_ports); | ||
| 1186 | if (ret) | ||
| 1187 | goto stream_error; | ||
| 1188 | |||
| 1189 | stream->state = SDW_STREAM_CONFIGURED; | ||
| 1190 | goto error; | ||
| 1191 | |||
| 1192 | stream_error: | ||
| 1193 | /* | ||
| 1194 | * we hit error so cleanup the stream, release all Slave(s) and | ||
| 1195 | * Master runtime | ||
| 1196 | */ | ||
| 1197 | sdw_release_master_stream(stream); | ||
| 1198 | error: | ||
| 1199 | mutex_unlock(&slave->bus->bus_lock); | ||
| 1200 | return ret; | ||
| 1201 | } | ||
| 1202 | EXPORT_SYMBOL(sdw_stream_add_slave); | ||
| 1203 | |||
| 1204 | /** | ||
| 1205 | * sdw_get_slave_dpn_prop() - Get Slave port capabilities | ||
| 1206 | * | ||
| 1207 | * @slave: Slave handle | ||
| 1208 | * @direction: Data direction. | ||
| 1209 | * @port_num: Port number | ||
| 1210 | */ | ||
| 1211 | struct sdw_dpn_prop *sdw_get_slave_dpn_prop(struct sdw_slave *slave, | ||
| 1212 | enum sdw_data_direction direction, | ||
| 1213 | unsigned int port_num) | ||
| 1214 | { | ||
| 1215 | struct sdw_dpn_prop *dpn_prop; | ||
| 1216 | u8 num_ports; | ||
| 1217 | int i; | ||
| 1218 | |||
| 1219 | if (direction == SDW_DATA_DIR_TX) { | ||
| 1220 | num_ports = hweight32(slave->prop.source_ports); | ||
| 1221 | dpn_prop = slave->prop.src_dpn_prop; | ||
| 1222 | } else { | ||
| 1223 | num_ports = hweight32(slave->prop.sink_ports); | ||
| 1224 | dpn_prop = slave->prop.sink_dpn_prop; | ||
| 1225 | } | ||
| 1226 | |||
| 1227 | for (i = 0; i < num_ports; i++) { | ||
| 1228 | dpn_prop = &dpn_prop[i]; | ||
| 1229 | |||
| 1230 | if (dpn_prop->num == port_num) | ||
| 1231 | return &dpn_prop[i]; | ||
| 1232 | } | ||
| 1233 | |||
| 1234 | return NULL; | ||
| 1235 | } | ||
| 1236 | |||
| 1237 | static int _sdw_prepare_stream(struct sdw_stream_runtime *stream) | ||
| 1238 | { | ||
| 1239 | struct sdw_master_runtime *m_rt = stream->m_rt; | ||
| 1240 | struct sdw_bus *bus = m_rt->bus; | ||
| 1241 | struct sdw_master_prop *prop = NULL; | ||
| 1242 | struct sdw_bus_params params; | ||
| 1243 | int ret; | ||
| 1244 | |||
| 1245 | prop = &bus->prop; | ||
| 1246 | memcpy(¶ms, &bus->params, sizeof(params)); | ||
| 1247 | |||
| 1248 | /* TODO: Support Asynchronous mode */ | ||
| 1249 | if ((prop->max_freq % stream->params.rate) != 0) { | ||
| 1250 | dev_err(bus->dev, "Async mode not supported"); | ||
| 1251 | return -EINVAL; | ||
| 1252 | } | ||
| 1253 | |||
| 1254 | /* Increment cumulative bus bandwidth */ | ||
| 1255 | /* TODO: Update this during Device-Device support */ | ||
| 1256 | bus->params.bandwidth += m_rt->stream->params.rate * | ||
| 1257 | m_rt->ch_count * m_rt->stream->params.bps; | ||
| 1258 | |||
| 1259 | /* Program params */ | ||
| 1260 | ret = sdw_program_params(bus); | ||
| 1261 | if (ret < 0) { | ||
| 1262 | dev_err(bus->dev, "Program params failed: %d", ret); | ||
| 1263 | goto restore_params; | ||
| 1264 | } | ||
| 1265 | |||
| 1266 | ret = do_bank_switch(stream); | ||
| 1267 | if (ret < 0) { | ||
| 1268 | dev_err(bus->dev, "Bank switch failed: %d", ret); | ||
| 1269 | goto restore_params; | ||
| 1270 | } | ||
| 1271 | |||
| 1272 | /* Prepare port(s) on the new clock configuration */ | ||
| 1273 | ret = sdw_prep_deprep_ports(m_rt, true); | ||
| 1274 | if (ret < 0) { | ||
| 1275 | dev_err(bus->dev, "Prepare port(s) failed ret = %d", | ||
| 1276 | ret); | ||
| 1277 | return ret; | ||
| 1278 | } | ||
| 1279 | |||
| 1280 | stream->state = SDW_STREAM_PREPARED; | ||
| 1281 | |||
| 1282 | return ret; | ||
| 1283 | |||
| 1284 | restore_params: | ||
| 1285 | memcpy(&bus->params, ¶ms, sizeof(params)); | ||
| 1286 | return ret; | ||
| 1287 | } | ||
| 1288 | |||
| 1289 | /** | ||
| 1290 | * sdw_prepare_stream() - Prepare SoundWire stream | ||
| 1291 | * | ||
| 1292 | * @stream: Soundwire stream | ||
| 1293 | * | ||
| 1294 | * Documentation/soundwire/stream.txt explains this API in detail | ||
| 1295 | */ | ||
| 1296 | int sdw_prepare_stream(struct sdw_stream_runtime *stream) | ||
| 1297 | { | ||
| 1298 | int ret = 0; | ||
| 1299 | |||
| 1300 | if (!stream) { | ||
| 1301 | pr_err("SoundWire: Handle not found for stream"); | ||
| 1302 | return -EINVAL; | ||
| 1303 | } | ||
| 1304 | |||
| 1305 | mutex_lock(&stream->m_rt->bus->bus_lock); | ||
| 1306 | |||
| 1307 | ret = _sdw_prepare_stream(stream); | ||
| 1308 | if (ret < 0) | ||
| 1309 | pr_err("Prepare for stream:%s failed: %d", stream->name, ret); | ||
| 1310 | |||
| 1311 | mutex_unlock(&stream->m_rt->bus->bus_lock); | ||
| 1312 | return ret; | ||
| 1313 | } | ||
| 1314 | EXPORT_SYMBOL(sdw_prepare_stream); | ||
| 1315 | |||
| 1316 | static int _sdw_enable_stream(struct sdw_stream_runtime *stream) | ||
| 1317 | { | ||
| 1318 | struct sdw_master_runtime *m_rt = stream->m_rt; | ||
| 1319 | struct sdw_bus *bus = m_rt->bus; | ||
| 1320 | int ret; | ||
| 1321 | |||
| 1322 | /* Program params */ | ||
| 1323 | ret = sdw_program_params(bus); | ||
| 1324 | if (ret < 0) { | ||
| 1325 | dev_err(bus->dev, "Program params failed: %d", ret); | ||
| 1326 | return ret; | ||
| 1327 | } | ||
| 1328 | |||
| 1329 | /* Enable port(s) */ | ||
| 1330 | ret = sdw_enable_disable_ports(m_rt, true); | ||
| 1331 | if (ret < 0) { | ||
| 1332 | dev_err(bus->dev, "Enable port(s) failed ret: %d", ret); | ||
| 1333 | return ret; | ||
| 1334 | } | ||
| 1335 | |||
| 1336 | ret = do_bank_switch(stream); | ||
| 1337 | if (ret < 0) { | ||
| 1338 | dev_err(bus->dev, "Bank switch failed: %d", ret); | ||
| 1339 | return ret; | ||
| 1340 | } | ||
| 1341 | |||
| 1342 | stream->state = SDW_STREAM_ENABLED; | ||
| 1343 | return 0; | ||
| 1344 | } | ||
| 1345 | |||
| 1346 | /** | ||
| 1347 | * sdw_enable_stream() - Enable SoundWire stream | ||
| 1348 | * | ||
| 1349 | * @stream: Soundwire stream | ||
| 1350 | * | ||
| 1351 | * Documentation/soundwire/stream.txt explains this API in detail | ||
| 1352 | */ | ||
| 1353 | int sdw_enable_stream(struct sdw_stream_runtime *stream) | ||
| 1354 | { | ||
| 1355 | int ret = 0; | ||
| 1356 | |||
| 1357 | if (!stream) { | ||
| 1358 | pr_err("SoundWire: Handle not found for stream"); | ||
| 1359 | return -EINVAL; | ||
| 1360 | } | ||
| 1361 | |||
| 1362 | mutex_lock(&stream->m_rt->bus->bus_lock); | ||
| 1363 | |||
| 1364 | ret = _sdw_enable_stream(stream); | ||
| 1365 | if (ret < 0) | ||
| 1366 | pr_err("Enable for stream:%s failed: %d", stream->name, ret); | ||
| 1367 | |||
| 1368 | mutex_unlock(&stream->m_rt->bus->bus_lock); | ||
| 1369 | return ret; | ||
| 1370 | } | ||
| 1371 | EXPORT_SYMBOL(sdw_enable_stream); | ||
| 1372 | |||
| 1373 | static int _sdw_disable_stream(struct sdw_stream_runtime *stream) | ||
| 1374 | { | ||
| 1375 | struct sdw_master_runtime *m_rt = stream->m_rt; | ||
| 1376 | struct sdw_bus *bus = m_rt->bus; | ||
| 1377 | int ret; | ||
| 1378 | |||
| 1379 | /* Disable port(s) */ | ||
| 1380 | ret = sdw_enable_disable_ports(m_rt, false); | ||
| 1381 | if (ret < 0) { | ||
| 1382 | dev_err(bus->dev, "Disable port(s) failed: %d", ret); | ||
| 1383 | return ret; | ||
| 1384 | } | ||
| 1385 | |||
| 1386 | stream->state = SDW_STREAM_DISABLED; | ||
| 1387 | |||
| 1388 | /* Program params */ | ||
| 1389 | ret = sdw_program_params(bus); | ||
| 1390 | if (ret < 0) { | ||
| 1391 | dev_err(bus->dev, "Program params failed: %d", ret); | ||
| 1392 | return ret; | ||
| 1393 | } | ||
| 1394 | |||
| 1395 | return do_bank_switch(stream); | ||
| 1396 | } | ||
| 1397 | |||
| 1398 | /** | ||
| 1399 | * sdw_disable_stream() - Disable SoundWire stream | ||
| 1400 | * | ||
| 1401 | * @stream: Soundwire stream | ||
| 1402 | * | ||
| 1403 | * Documentation/soundwire/stream.txt explains this API in detail | ||
| 1404 | */ | ||
| 1405 | int sdw_disable_stream(struct sdw_stream_runtime *stream) | ||
| 1406 | { | ||
| 1407 | int ret = 0; | ||
| 1408 | |||
| 1409 | if (!stream) { | ||
| 1410 | pr_err("SoundWire: Handle not found for stream"); | ||
| 1411 | return -EINVAL; | ||
| 1412 | } | ||
| 1413 | |||
| 1414 | mutex_lock(&stream->m_rt->bus->bus_lock); | ||
| 1415 | |||
| 1416 | ret = _sdw_disable_stream(stream); | ||
| 1417 | if (ret < 0) | ||
| 1418 | pr_err("Disable for stream:%s failed: %d", stream->name, ret); | ||
| 1419 | |||
| 1420 | mutex_unlock(&stream->m_rt->bus->bus_lock); | ||
| 1421 | return ret; | ||
| 1422 | } | ||
| 1423 | EXPORT_SYMBOL(sdw_disable_stream); | ||
| 1424 | |||
| 1425 | static int _sdw_deprepare_stream(struct sdw_stream_runtime *stream) | ||
| 1426 | { | ||
| 1427 | struct sdw_master_runtime *m_rt = stream->m_rt; | ||
| 1428 | struct sdw_bus *bus = m_rt->bus; | ||
| 1429 | int ret = 0; | ||
| 1430 | |||
| 1431 | /* De-prepare port(s) */ | ||
| 1432 | ret = sdw_prep_deprep_ports(m_rt, false); | ||
| 1433 | if (ret < 0) { | ||
| 1434 | dev_err(bus->dev, "De-prepare port(s) failed: %d", ret); | ||
| 1435 | return ret; | ||
| 1436 | } | ||
| 1437 | |||
| 1438 | stream->state = SDW_STREAM_DEPREPARED; | ||
| 1439 | |||
| 1440 | /* TODO: Update this during Device-Device support */ | ||
| 1441 | bus->params.bandwidth -= m_rt->stream->params.rate * | ||
| 1442 | m_rt->ch_count * m_rt->stream->params.bps; | ||
| 1443 | |||
| 1444 | /* Program params */ | ||
| 1445 | ret = sdw_program_params(bus); | ||
| 1446 | if (ret < 0) { | ||
| 1447 | dev_err(bus->dev, "Program params failed: %d", ret); | ||
| 1448 | return ret; | ||
| 1449 | } | ||
| 1450 | |||
| 1451 | return do_bank_switch(stream); | ||
| 1452 | } | ||
| 1453 | |||
| 1454 | /** | ||
| 1455 | * sdw_deprepare_stream() - Deprepare SoundWire stream | ||
| 1456 | * | ||
| 1457 | * @stream: Soundwire stream | ||
| 1458 | * | ||
| 1459 | * Documentation/soundwire/stream.txt explains this API in detail | ||
| 1460 | */ | ||
| 1461 | int sdw_deprepare_stream(struct sdw_stream_runtime *stream) | ||
| 1462 | { | ||
| 1463 | int ret = 0; | ||
| 1464 | |||
| 1465 | if (!stream) { | ||
| 1466 | pr_err("SoundWire: Handle not found for stream"); | ||
| 1467 | return -EINVAL; | ||
| 1468 | } | ||
| 1469 | |||
| 1470 | mutex_lock(&stream->m_rt->bus->bus_lock); | ||
| 1471 | |||
| 1472 | ret = _sdw_deprepare_stream(stream); | ||
| 1473 | if (ret < 0) | ||
| 1474 | pr_err("De-prepare for stream:%d failed: %d", ret, ret); | ||
| 1475 | |||
| 1476 | mutex_unlock(&stream->m_rt->bus->bus_lock); | ||
| 1477 | return ret; | ||
| 1478 | } | ||
| 1479 | EXPORT_SYMBOL(sdw_deprepare_stream); | ||
diff --git a/drivers/uio/uio.c b/drivers/uio/uio.c index fd4848392e0d..e8f4ac9400ea 100644 --- a/drivers/uio/uio.c +++ b/drivers/uio/uio.c | |||
| @@ -270,7 +270,7 @@ static int uio_dev_add_attributes(struct uio_device *idev) | |||
| 270 | if (!map_found) { | 270 | if (!map_found) { |
| 271 | map_found = 1; | 271 | map_found = 1; |
| 272 | idev->map_dir = kobject_create_and_add("maps", | 272 | idev->map_dir = kobject_create_and_add("maps", |
| 273 | &idev->dev->kobj); | 273 | &idev->dev.kobj); |
| 274 | if (!idev->map_dir) { | 274 | if (!idev->map_dir) { |
| 275 | ret = -ENOMEM; | 275 | ret = -ENOMEM; |
| 276 | goto err_map; | 276 | goto err_map; |
| @@ -299,7 +299,7 @@ static int uio_dev_add_attributes(struct uio_device *idev) | |||
| 299 | if (!portio_found) { | 299 | if (!portio_found) { |
| 300 | portio_found = 1; | 300 | portio_found = 1; |
| 301 | idev->portio_dir = kobject_create_and_add("portio", | 301 | idev->portio_dir = kobject_create_and_add("portio", |
| 302 | &idev->dev->kobj); | 302 | &idev->dev.kobj); |
| 303 | if (!idev->portio_dir) { | 303 | if (!idev->portio_dir) { |
| 304 | ret = -ENOMEM; | 304 | ret = -ENOMEM; |
| 305 | goto err_portio; | 305 | goto err_portio; |
| @@ -342,7 +342,7 @@ err_map_kobj: | |||
| 342 | kobject_put(&map->kobj); | 342 | kobject_put(&map->kobj); |
| 343 | } | 343 | } |
| 344 | kobject_put(idev->map_dir); | 344 | kobject_put(idev->map_dir); |
| 345 | dev_err(idev->dev, "error creating sysfs files (%d)\n", ret); | 345 | dev_err(&idev->dev, "error creating sysfs files (%d)\n", ret); |
| 346 | return ret; | 346 | return ret; |
| 347 | } | 347 | } |
| 348 | 348 | ||
| @@ -379,7 +379,7 @@ static int uio_get_minor(struct uio_device *idev) | |||
| 379 | idev->minor = retval; | 379 | idev->minor = retval; |
| 380 | retval = 0; | 380 | retval = 0; |
| 381 | } else if (retval == -ENOSPC) { | 381 | } else if (retval == -ENOSPC) { |
| 382 | dev_err(idev->dev, "too many uio devices\n"); | 382 | dev_err(&idev->dev, "too many uio devices\n"); |
| 383 | retval = -EINVAL; | 383 | retval = -EINVAL; |
| 384 | } | 384 | } |
| 385 | mutex_unlock(&minor_lock); | 385 | mutex_unlock(&minor_lock); |
| @@ -433,6 +433,7 @@ static int uio_open(struct inode *inode, struct file *filep) | |||
| 433 | struct uio_device *idev; | 433 | struct uio_device *idev; |
| 434 | struct uio_listener *listener; | 434 | struct uio_listener *listener; |
| 435 | int ret = 0; | 435 | int ret = 0; |
| 436 | unsigned long flags; | ||
| 436 | 437 | ||
| 437 | mutex_lock(&minor_lock); | 438 | mutex_lock(&minor_lock); |
| 438 | idev = idr_find(&uio_idr, iminor(inode)); | 439 | idev = idr_find(&uio_idr, iminor(inode)); |
| @@ -442,9 +443,11 @@ static int uio_open(struct inode *inode, struct file *filep) | |||
| 442 | goto out; | 443 | goto out; |
| 443 | } | 444 | } |
| 444 | 445 | ||
| 446 | get_device(&idev->dev); | ||
| 447 | |||
| 445 | if (!try_module_get(idev->owner)) { | 448 | if (!try_module_get(idev->owner)) { |
| 446 | ret = -ENODEV; | 449 | ret = -ENODEV; |
| 447 | goto out; | 450 | goto err_module_get; |
| 448 | } | 451 | } |
| 449 | 452 | ||
| 450 | listener = kmalloc(sizeof(*listener), GFP_KERNEL); | 453 | listener = kmalloc(sizeof(*listener), GFP_KERNEL); |
| @@ -457,11 +460,13 @@ static int uio_open(struct inode *inode, struct file *filep) | |||
| 457 | listener->event_count = atomic_read(&idev->event); | 460 | listener->event_count = atomic_read(&idev->event); |
| 458 | filep->private_data = listener; | 461 | filep->private_data = listener; |
| 459 | 462 | ||
| 460 | if (idev->info->open) { | 463 | spin_lock_irqsave(&idev->info_lock, flags); |
| 464 | if (idev->info && idev->info->open) | ||
| 461 | ret = idev->info->open(idev->info, inode); | 465 | ret = idev->info->open(idev->info, inode); |
| 462 | if (ret) | 466 | spin_unlock_irqrestore(&idev->info_lock, flags); |
| 463 | goto err_infoopen; | 467 | if (ret) |
| 464 | } | 468 | goto err_infoopen; |
| 469 | |||
| 465 | return 0; | 470 | return 0; |
| 466 | 471 | ||
| 467 | err_infoopen: | 472 | err_infoopen: |
| @@ -470,6 +475,9 @@ err_infoopen: | |||
| 470 | err_alloc_listener: | 475 | err_alloc_listener: |
| 471 | module_put(idev->owner); | 476 | module_put(idev->owner); |
| 472 | 477 | ||
| 478 | err_module_get: | ||
| 479 | put_device(&idev->dev); | ||
| 480 | |||
| 473 | out: | 481 | out: |
| 474 | return ret; | 482 | return ret; |
| 475 | } | 483 | } |
| @@ -487,12 +495,16 @@ static int uio_release(struct inode *inode, struct file *filep) | |||
| 487 | int ret = 0; | 495 | int ret = 0; |
| 488 | struct uio_listener *listener = filep->private_data; | 496 | struct uio_listener *listener = filep->private_data; |
| 489 | struct uio_device *idev = listener->dev; | 497 | struct uio_device *idev = listener->dev; |
| 498 | unsigned long flags; | ||
| 490 | 499 | ||
| 491 | if (idev->info->release) | 500 | spin_lock_irqsave(&idev->info_lock, flags); |
| 501 | if (idev->info && idev->info->release) | ||
| 492 | ret = idev->info->release(idev->info, inode); | 502 | ret = idev->info->release(idev->info, inode); |
| 503 | spin_unlock_irqrestore(&idev->info_lock, flags); | ||
| 493 | 504 | ||
| 494 | module_put(idev->owner); | 505 | module_put(idev->owner); |
| 495 | kfree(listener); | 506 | kfree(listener); |
| 507 | put_device(&idev->dev); | ||
| 496 | return ret; | 508 | return ret; |
| 497 | } | 509 | } |
| 498 | 510 | ||
| @@ -500,9 +512,16 @@ static __poll_t uio_poll(struct file *filep, poll_table *wait) | |||
| 500 | { | 512 | { |
| 501 | struct uio_listener *listener = filep->private_data; | 513 | struct uio_listener *listener = filep->private_data; |
| 502 | struct uio_device *idev = listener->dev; | 514 | struct uio_device *idev = listener->dev; |
| 515 | __poll_t ret = 0; | ||
| 516 | unsigned long flags; | ||
| 503 | 517 | ||
| 504 | if (!idev->info->irq) | 518 | spin_lock_irqsave(&idev->info_lock, flags); |
| 505 | return -EIO; | 519 | if (!idev->info || !idev->info->irq) |
| 520 | ret = -EIO; | ||
| 521 | spin_unlock_irqrestore(&idev->info_lock, flags); | ||
| 522 | |||
| 523 | if (ret) | ||
| 524 | return ret; | ||
| 506 | 525 | ||
| 507 | poll_wait(filep, &idev->wait, wait); | 526 | poll_wait(filep, &idev->wait, wait); |
| 508 | if (listener->event_count != atomic_read(&idev->event)) | 527 | if (listener->event_count != atomic_read(&idev->event)) |
| @@ -516,11 +535,17 @@ static ssize_t uio_read(struct file *filep, char __user *buf, | |||
| 516 | struct uio_listener *listener = filep->private_data; | 535 | struct uio_listener *listener = filep->private_data; |
| 517 | struct uio_device *idev = listener->dev; | 536 | struct uio_device *idev = listener->dev; |
| 518 | DECLARE_WAITQUEUE(wait, current); | 537 | DECLARE_WAITQUEUE(wait, current); |
| 519 | ssize_t retval; | 538 | ssize_t retval = 0; |
| 520 | s32 event_count; | 539 | s32 event_count; |
| 540 | unsigned long flags; | ||
| 521 | 541 | ||
| 522 | if (!idev->info->irq) | 542 | spin_lock_irqsave(&idev->info_lock, flags); |
| 523 | return -EIO; | 543 | if (!idev->info || !idev->info->irq) |
| 544 | retval = -EIO; | ||
| 545 | spin_unlock_irqrestore(&idev->info_lock, flags); | ||
| 546 | |||
| 547 | if (retval) | ||
| 548 | return retval; | ||
| 524 | 549 | ||
| 525 | if (count != sizeof(s32)) | 550 | if (count != sizeof(s32)) |
| 526 | return -EINVAL; | 551 | return -EINVAL; |
| @@ -567,21 +592,33 @@ static ssize_t uio_write(struct file *filep, const char __user *buf, | |||
| 567 | struct uio_device *idev = listener->dev; | 592 | struct uio_device *idev = listener->dev; |
| 568 | ssize_t retval; | 593 | ssize_t retval; |
| 569 | s32 irq_on; | 594 | s32 irq_on; |
| 595 | unsigned long flags; | ||
| 570 | 596 | ||
| 571 | if (!idev->info->irq) | 597 | spin_lock_irqsave(&idev->info_lock, flags); |
| 572 | return -EIO; | 598 | if (!idev->info || !idev->info->irq) { |
| 599 | retval = -EIO; | ||
| 600 | goto out; | ||
| 601 | } | ||
| 573 | 602 | ||
| 574 | if (count != sizeof(s32)) | 603 | if (count != sizeof(s32)) { |
| 575 | return -EINVAL; | 604 | retval = -EINVAL; |
| 605 | goto out; | ||
| 606 | } | ||
| 576 | 607 | ||
| 577 | if (!idev->info->irqcontrol) | 608 | if (!idev->info->irqcontrol) { |
| 578 | return -ENOSYS; | 609 | retval = -ENOSYS; |
| 610 | goto out; | ||
| 611 | } | ||
| 579 | 612 | ||
| 580 | if (copy_from_user(&irq_on, buf, count)) | 613 | if (copy_from_user(&irq_on, buf, count)) { |
| 581 | return -EFAULT; | 614 | retval = -EFAULT; |
| 615 | goto out; | ||
| 616 | } | ||
| 582 | 617 | ||
| 583 | retval = idev->info->irqcontrol(idev->info, irq_on); | 618 | retval = idev->info->irqcontrol(idev->info, irq_on); |
| 584 | 619 | ||
| 620 | out: | ||
| 621 | spin_unlock_irqrestore(&idev->info_lock, flags); | ||
| 585 | return retval ? retval : sizeof(s32); | 622 | return retval ? retval : sizeof(s32); |
| 586 | } | 623 | } |
| 587 | 624 | ||
| @@ -597,7 +634,7 @@ static int uio_find_mem_index(struct vm_area_struct *vma) | |||
| 597 | return -1; | 634 | return -1; |
| 598 | } | 635 | } |
| 599 | 636 | ||
| 600 | static int uio_vma_fault(struct vm_fault *vmf) | 637 | static vm_fault_t uio_vma_fault(struct vm_fault *vmf) |
| 601 | { | 638 | { |
| 602 | struct uio_device *idev = vmf->vma->vm_private_data; | 639 | struct uio_device *idev = vmf->vma->vm_private_data; |
| 603 | struct page *page; | 640 | struct page *page; |
| @@ -794,6 +831,13 @@ static void release_uio_class(void) | |||
| 794 | uio_major_cleanup(); | 831 | uio_major_cleanup(); |
| 795 | } | 832 | } |
| 796 | 833 | ||
| 834 | static void uio_device_release(struct device *dev) | ||
| 835 | { | ||
| 836 | struct uio_device *idev = dev_get_drvdata(dev); | ||
| 837 | |||
| 838 | kfree(idev); | ||
| 839 | } | ||
| 840 | |||
| 797 | /** | 841 | /** |
| 798 | * uio_register_device - register a new userspace IO device | 842 | * uio_register_device - register a new userspace IO device |
| 799 | * @owner: module that creates the new device | 843 | * @owner: module that creates the new device |
| @@ -814,13 +858,14 @@ int __uio_register_device(struct module *owner, | |||
| 814 | 858 | ||
| 815 | info->uio_dev = NULL; | 859 | info->uio_dev = NULL; |
| 816 | 860 | ||
| 817 | idev = devm_kzalloc(parent, sizeof(*idev), GFP_KERNEL); | 861 | idev = kzalloc(sizeof(*idev), GFP_KERNEL); |
| 818 | if (!idev) { | 862 | if (!idev) { |
| 819 | return -ENOMEM; | 863 | return -ENOMEM; |
| 820 | } | 864 | } |
| 821 | 865 | ||
| 822 | idev->owner = owner; | 866 | idev->owner = owner; |
| 823 | idev->info = info; | 867 | idev->info = info; |
| 868 | spin_lock_init(&idev->info_lock); | ||
| 824 | init_waitqueue_head(&idev->wait); | 869 | init_waitqueue_head(&idev->wait); |
| 825 | atomic_set(&idev->event, 0); | 870 | atomic_set(&idev->event, 0); |
| 826 | 871 | ||
| @@ -828,14 +873,19 @@ int __uio_register_device(struct module *owner, | |||
| 828 | if (ret) | 873 | if (ret) |
| 829 | return ret; | 874 | return ret; |
| 830 | 875 | ||
| 831 | idev->dev = device_create(&uio_class, parent, | 876 | idev->dev.devt = MKDEV(uio_major, idev->minor); |
| 832 | MKDEV(uio_major, idev->minor), idev, | 877 | idev->dev.class = &uio_class; |
| 833 | "uio%d", idev->minor); | 878 | idev->dev.parent = parent; |
| 834 | if (IS_ERR(idev->dev)) { | 879 | idev->dev.release = uio_device_release; |
| 835 | printk(KERN_ERR "UIO: device register failed\n"); | 880 | dev_set_drvdata(&idev->dev, idev); |
| 836 | ret = PTR_ERR(idev->dev); | 881 | |
| 882 | ret = dev_set_name(&idev->dev, "uio%d", idev->minor); | ||
| 883 | if (ret) | ||
| 884 | goto err_device_create; | ||
| 885 | |||
| 886 | ret = device_register(&idev->dev); | ||
| 887 | if (ret) | ||
| 837 | goto err_device_create; | 888 | goto err_device_create; |
| 838 | } | ||
| 839 | 889 | ||
| 840 | ret = uio_dev_add_attributes(idev); | 890 | ret = uio_dev_add_attributes(idev); |
| 841 | if (ret) | 891 | if (ret) |
| @@ -863,7 +913,7 @@ int __uio_register_device(struct module *owner, | |||
| 863 | err_request_irq: | 913 | err_request_irq: |
| 864 | uio_dev_del_attributes(idev); | 914 | uio_dev_del_attributes(idev); |
| 865 | err_uio_dev_add_attributes: | 915 | err_uio_dev_add_attributes: |
| 866 | device_destroy(&uio_class, MKDEV(uio_major, idev->minor)); | 916 | device_unregister(&idev->dev); |
| 867 | err_device_create: | 917 | err_device_create: |
| 868 | uio_free_minor(idev); | 918 | uio_free_minor(idev); |
| 869 | return ret; | 919 | return ret; |
| @@ -878,6 +928,7 @@ EXPORT_SYMBOL_GPL(__uio_register_device); | |||
| 878 | void uio_unregister_device(struct uio_info *info) | 928 | void uio_unregister_device(struct uio_info *info) |
| 879 | { | 929 | { |
| 880 | struct uio_device *idev; | 930 | struct uio_device *idev; |
| 931 | unsigned long flags; | ||
| 881 | 932 | ||
| 882 | if (!info || !info->uio_dev) | 933 | if (!info || !info->uio_dev) |
| 883 | return; | 934 | return; |
| @@ -891,7 +942,11 @@ void uio_unregister_device(struct uio_info *info) | |||
| 891 | if (info->irq && info->irq != UIO_IRQ_CUSTOM) | 942 | if (info->irq && info->irq != UIO_IRQ_CUSTOM) |
| 892 | free_irq(info->irq, idev); | 943 | free_irq(info->irq, idev); |
| 893 | 944 | ||
| 894 | device_destroy(&uio_class, MKDEV(uio_major, idev->minor)); | 945 | spin_lock_irqsave(&idev->info_lock, flags); |
| 946 | idev->info = NULL; | ||
| 947 | spin_unlock_irqrestore(&idev->info_lock, flags); | ||
| 948 | |||
| 949 | device_unregister(&idev->dev); | ||
| 895 | 950 | ||
| 896 | return; | 951 | return; |
| 897 | } | 952 | } |
diff --git a/drivers/uio/uio_fsl_elbc_gpcm.c b/drivers/uio/uio_fsl_elbc_gpcm.c index b46323d9dc18..b55191335d90 100644 --- a/drivers/uio/uio_fsl_elbc_gpcm.c +++ b/drivers/uio/uio_fsl_elbc_gpcm.c | |||
| @@ -475,7 +475,6 @@ MODULE_DEVICE_TABLE(of, uio_fsl_elbc_gpcm_match); | |||
| 475 | static struct platform_driver uio_fsl_elbc_gpcm_driver = { | 475 | static struct platform_driver uio_fsl_elbc_gpcm_driver = { |
| 476 | .driver = { | 476 | .driver = { |
| 477 | .name = "fsl,elbc-gpcm-uio", | 477 | .name = "fsl,elbc-gpcm-uio", |
| 478 | .owner = THIS_MODULE, | ||
| 479 | .of_match_table = uio_fsl_elbc_gpcm_match, | 478 | .of_match_table = uio_fsl_elbc_gpcm_match, |
| 480 | }, | 479 | }, |
| 481 | .probe = uio_fsl_elbc_gpcm_probe, | 480 | .probe = uio_fsl_elbc_gpcm_probe, |
diff --git a/drivers/virt/vboxguest/vboxguest_linux.c b/drivers/virt/vboxguest/vboxguest_linux.c index 398d22693234..6e2a9619192d 100644 --- a/drivers/virt/vboxguest/vboxguest_linux.c +++ b/drivers/virt/vboxguest/vboxguest_linux.c | |||
| @@ -121,7 +121,9 @@ static long vbg_misc_device_ioctl(struct file *filp, unsigned int req, | |||
| 121 | if (!buf) | 121 | if (!buf) |
| 122 | return -ENOMEM; | 122 | return -ENOMEM; |
| 123 | 123 | ||
| 124 | if (copy_from_user(buf, (void *)arg, hdr.size_in)) { | 124 | *((struct vbg_ioctl_hdr *)buf) = hdr; |
| 125 | if (copy_from_user(buf + sizeof(hdr), (void *)arg + sizeof(hdr), | ||
| 126 | hdr.size_in - sizeof(hdr))) { | ||
| 125 | ret = -EFAULT; | 127 | ret = -EFAULT; |
| 126 | goto out; | 128 | goto out; |
| 127 | } | 129 | } |
diff --git a/drivers/w1/masters/mxc_w1.c b/drivers/w1/masters/mxc_w1.c index 74f2e6e6202a..8851d441e5fd 100644 --- a/drivers/w1/masters/mxc_w1.c +++ b/drivers/w1/masters/mxc_w1.c | |||
| @@ -112,6 +112,10 @@ static int mxc_w1_probe(struct platform_device *pdev) | |||
| 112 | if (IS_ERR(mdev->clk)) | 112 | if (IS_ERR(mdev->clk)) |
| 113 | return PTR_ERR(mdev->clk); | 113 | return PTR_ERR(mdev->clk); |
| 114 | 114 | ||
| 115 | err = clk_prepare_enable(mdev->clk); | ||
| 116 | if (err) | ||
| 117 | return err; | ||
| 118 | |||
| 115 | clkrate = clk_get_rate(mdev->clk); | 119 | clkrate = clk_get_rate(mdev->clk); |
| 116 | if (clkrate < 10000000) | 120 | if (clkrate < 10000000) |
| 117 | dev_warn(&pdev->dev, | 121 | dev_warn(&pdev->dev, |
| @@ -125,12 +129,10 @@ static int mxc_w1_probe(struct platform_device *pdev) | |||
| 125 | 129 | ||
| 126 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 130 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
| 127 | mdev->regs = devm_ioremap_resource(&pdev->dev, res); | 131 | mdev->regs = devm_ioremap_resource(&pdev->dev, res); |
| 128 | if (IS_ERR(mdev->regs)) | 132 | if (IS_ERR(mdev->regs)) { |
| 129 | return PTR_ERR(mdev->regs); | 133 | err = PTR_ERR(mdev->regs); |
| 130 | 134 | goto out_disable_clk; | |
| 131 | err = clk_prepare_enable(mdev->clk); | 135 | } |
| 132 | if (err) | ||
| 133 | return err; | ||
| 134 | 136 | ||
| 135 | /* Software reset 1-Wire module */ | 137 | /* Software reset 1-Wire module */ |
| 136 | writeb(MXC_W1_RESET_RST, mdev->regs + MXC_W1_RESET); | 138 | writeb(MXC_W1_RESET_RST, mdev->regs + MXC_W1_RESET); |
| @@ -146,8 +148,12 @@ static int mxc_w1_probe(struct platform_device *pdev) | |||
| 146 | 148 | ||
| 147 | err = w1_add_master_device(&mdev->bus_master); | 149 | err = w1_add_master_device(&mdev->bus_master); |
| 148 | if (err) | 150 | if (err) |
| 149 | clk_disable_unprepare(mdev->clk); | 151 | goto out_disable_clk; |
| 150 | 152 | ||
| 153 | return 0; | ||
| 154 | |||
| 155 | out_disable_clk: | ||
| 156 | clk_disable_unprepare(mdev->clk); | ||
| 151 | return err; | 157 | return err; |
| 152 | } | 158 | } |
| 153 | 159 | ||
diff --git a/drivers/w1/w1.c b/drivers/w1/w1.c index 80a778b02f28..caef0e0fd817 100644 --- a/drivers/w1/w1.c +++ b/drivers/w1/w1.c | |||
| @@ -751,7 +751,7 @@ int w1_attach_slave_device(struct w1_master *dev, struct w1_reg_num *rn) | |||
| 751 | 751 | ||
| 752 | /* slave modules need to be loaded in a context with unlocked mutex */ | 752 | /* slave modules need to be loaded in a context with unlocked mutex */ |
| 753 | mutex_unlock(&dev->mutex); | 753 | mutex_unlock(&dev->mutex); |
| 754 | request_module("w1-family-0x%02x", rn->family); | 754 | request_module("w1-family-0x%02X", rn->family); |
| 755 | mutex_lock(&dev->mutex); | 755 | mutex_lock(&dev->mutex); |
| 756 | 756 | ||
| 757 | spin_lock(&w1_flock); | 757 | spin_lock(&w1_flock); |
diff --git a/include/linux/coresight.h b/include/linux/coresight.h index d950dad5056a..c265e0468414 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h | |||
| @@ -1,13 +1,6 @@ | |||
| 1 | /* Copyright (c) 2012, The Linux Foundation. All rights reserved. | 1 | /* SPDX-License-Identifier: GPL-2.0 */ |
| 2 | * | 2 | /* |
| 3 | * This program is free software; you can redistribute it and/or modify | 3 | * Copyright (c) 2012, The Linux Foundation. All rights reserved. |
| 4 | * it under the terms of the GNU General Public License version 2 and | ||
| 5 | * only version 2 as published by the Free Software Foundation. | ||
| 6 | * | ||
| 7 | * This program is distributed in the hope that it will be useful, | ||
| 8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 10 | * GNU General Public License for more details. | ||
| 11 | */ | 4 | */ |
| 12 | 5 | ||
| 13 | #ifndef _LINUX_CORESIGHT_H | 6 | #ifndef _LINUX_CORESIGHT_H |
diff --git a/include/linux/fpga/altera-pr-ip-core.h b/include/linux/fpga/altera-pr-ip-core.h index 3810a9033f49..7d4664730d60 100644 --- a/include/linux/fpga/altera-pr-ip-core.h +++ b/include/linux/fpga/altera-pr-ip-core.h | |||
| @@ -1,3 +1,4 @@ | |||
| 1 | /* SPDX-License-Identifier: GPL-2.0 */ | ||
| 1 | /* | 2 | /* |
| 2 | * Driver for Altera Partial Reconfiguration IP Core | 3 | * Driver for Altera Partial Reconfiguration IP Core |
| 3 | * | 4 | * |
| @@ -5,18 +6,6 @@ | |||
| 5 | * | 6 | * |
| 6 | * Based on socfpga-a10.c Copyright (C) 2015-2016 Altera Corporation | 7 | * Based on socfpga-a10.c Copyright (C) 2015-2016 Altera Corporation |
| 7 | * by Alan Tull <atull@opensource.altera.com> | 8 | * by Alan Tull <atull@opensource.altera.com> |
| 8 | * | ||
| 9 | * This program is free software; you can redistribute it and/or modify it | ||
| 10 | * under the terms and conditions of the GNU General Public License, | ||
| 11 | * version 2, as published by the Free Software Foundation. | ||
| 12 | * | ||
| 13 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
| 14 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 15 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 16 | * more details. | ||
| 17 | * | ||
| 18 | * You should have received a copy of the GNU General Public License along with | ||
| 19 | * this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 20 | */ | 9 | */ |
| 21 | 10 | ||
| 22 | #ifndef _ALT_PR_IP_CORE_H | 11 | #ifndef _ALT_PR_IP_CORE_H |
diff --git a/include/linux/fpga/fpga-bridge.h b/include/linux/fpga/fpga-bridge.h index 3694821a6d2d..ce550fcf6360 100644 --- a/include/linux/fpga/fpga-bridge.h +++ b/include/linux/fpga/fpga-bridge.h | |||
| @@ -62,8 +62,11 @@ int of_fpga_bridge_get_to_list(struct device_node *np, | |||
| 62 | struct fpga_image_info *info, | 62 | struct fpga_image_info *info, |
| 63 | struct list_head *bridge_list); | 63 | struct list_head *bridge_list); |
| 64 | 64 | ||
| 65 | int fpga_bridge_register(struct device *dev, const char *name, | 65 | struct fpga_bridge *fpga_bridge_create(struct device *dev, const char *name, |
| 66 | const struct fpga_bridge_ops *br_ops, void *priv); | 66 | const struct fpga_bridge_ops *br_ops, |
| 67 | void fpga_bridge_unregister(struct device *dev); | 67 | void *priv); |
| 68 | void fpga_bridge_free(struct fpga_bridge *br); | ||
| 69 | int fpga_bridge_register(struct fpga_bridge *br); | ||
| 70 | void fpga_bridge_unregister(struct fpga_bridge *br); | ||
| 68 | 71 | ||
| 69 | #endif /* _LINUX_FPGA_BRIDGE_H */ | 72 | #endif /* _LINUX_FPGA_BRIDGE_H */ |
diff --git a/include/linux/fpga/fpga-mgr.h b/include/linux/fpga/fpga-mgr.h index 3c6de23aabdf..eec7c2478b0d 100644 --- a/include/linux/fpga/fpga-mgr.h +++ b/include/linux/fpga/fpga-mgr.h | |||
| @@ -1,20 +1,9 @@ | |||
| 1 | /* SPDX-License-Identifier: GPL-2.0 */ | ||
| 1 | /* | 2 | /* |
| 2 | * FPGA Framework | 3 | * FPGA Framework |
| 3 | * | 4 | * |
| 4 | * Copyright (C) 2013-2016 Altera Corporation | 5 | * Copyright (C) 2013-2016 Altera Corporation |
| 5 | * Copyright (C) 2017 Intel Corporation | 6 | * Copyright (C) 2017 Intel Corporation |
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or modify it | ||
| 8 | * under the terms and conditions of the GNU General Public License, | ||
| 9 | * version 2, as published by the Free Software Foundation. | ||
| 10 | * | ||
| 11 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 14 | * more details. | ||
| 15 | * | ||
| 16 | * You should have received a copy of the GNU General Public License along with | ||
| 17 | * this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 18 | */ | 7 | */ |
| 19 | #ifndef _LINUX_FPGA_MGR_H | 8 | #ifndef _LINUX_FPGA_MGR_H |
| 20 | #define _LINUX_FPGA_MGR_H | 9 | #define _LINUX_FPGA_MGR_H |
| @@ -170,9 +159,11 @@ struct fpga_manager *fpga_mgr_get(struct device *dev); | |||
| 170 | 159 | ||
| 171 | void fpga_mgr_put(struct fpga_manager *mgr); | 160 | void fpga_mgr_put(struct fpga_manager *mgr); |
| 172 | 161 | ||
| 173 | int fpga_mgr_register(struct device *dev, const char *name, | 162 | struct fpga_manager *fpga_mgr_create(struct device *dev, const char *name, |
| 174 | const struct fpga_manager_ops *mops, void *priv); | 163 | const struct fpga_manager_ops *mops, |
| 175 | 164 | void *priv); | |
| 176 | void fpga_mgr_unregister(struct device *dev); | 165 | void fpga_mgr_free(struct fpga_manager *mgr); |
| 166 | int fpga_mgr_register(struct fpga_manager *mgr); | ||
| 167 | void fpga_mgr_unregister(struct fpga_manager *mgr); | ||
| 177 | 168 | ||
| 178 | #endif /*_LINUX_FPGA_MGR_H */ | 169 | #endif /*_LINUX_FPGA_MGR_H */ |
diff --git a/include/linux/fpga/fpga-region.h b/include/linux/fpga/fpga-region.h index b6520318ab9c..d7071cddd727 100644 --- a/include/linux/fpga/fpga-region.h +++ b/include/linux/fpga/fpga-region.h | |||
| @@ -1,3 +1,5 @@ | |||
| 1 | /* SPDX-License-Identifier: GPL-2.0 */ | ||
| 2 | |||
| 1 | #ifndef _FPGA_REGION_H | 3 | #ifndef _FPGA_REGION_H |
| 2 | #define _FPGA_REGION_H | 4 | #define _FPGA_REGION_H |
| 3 | 5 | ||
| @@ -14,7 +16,6 @@ | |||
| 14 | * @info: FPGA image info | 16 | * @info: FPGA image info |
| 15 | * @priv: private data | 17 | * @priv: private data |
| 16 | * @get_bridges: optional function to get bridges to a list | 18 | * @get_bridges: optional function to get bridges to a list |
| 17 | * @groups: optional attribute groups. | ||
| 18 | */ | 19 | */ |
| 19 | struct fpga_region { | 20 | struct fpga_region { |
| 20 | struct device dev; | 21 | struct device dev; |
| @@ -24,7 +25,6 @@ struct fpga_region { | |||
| 24 | struct fpga_image_info *info; | 25 | struct fpga_image_info *info; |
| 25 | void *priv; | 26 | void *priv; |
| 26 | int (*get_bridges)(struct fpga_region *region); | 27 | int (*get_bridges)(struct fpga_region *region); |
| 27 | const struct attribute_group **groups; | ||
| 28 | }; | 28 | }; |
| 29 | 29 | ||
| 30 | #define to_fpga_region(d) container_of(d, struct fpga_region, dev) | 30 | #define to_fpga_region(d) container_of(d, struct fpga_region, dev) |
| @@ -34,7 +34,12 @@ struct fpga_region *fpga_region_class_find( | |||
| 34 | int (*match)(struct device *, const void *)); | 34 | int (*match)(struct device *, const void *)); |
| 35 | 35 | ||
| 36 | int fpga_region_program_fpga(struct fpga_region *region); | 36 | int fpga_region_program_fpga(struct fpga_region *region); |
| 37 | int fpga_region_register(struct device *dev, struct fpga_region *region); | 37 | |
| 38 | int fpga_region_unregister(struct fpga_region *region); | 38 | struct fpga_region |
| 39 | *fpga_region_create(struct device *dev, struct fpga_manager *mgr, | ||
| 40 | int (*get_bridges)(struct fpga_region *)); | ||
| 41 | void fpga_region_free(struct fpga_region *region); | ||
| 42 | int fpga_region_register(struct fpga_region *region); | ||
| 43 | void fpga_region_unregister(struct fpga_region *region); | ||
| 39 | 44 | ||
| 40 | #endif /* _FPGA_REGION_H */ | 45 | #endif /* _FPGA_REGION_H */ |
diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index 192ed8fbc403..11b5612dc066 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h | |||
| @@ -163,6 +163,7 @@ static inline u32 hv_get_bytes_to_write(const struct hv_ring_buffer_info *rbi) | |||
| 163 | * 2 . 4 (Windows 8) | 163 | * 2 . 4 (Windows 8) |
| 164 | * 3 . 0 (Windows 8 R2) | 164 | * 3 . 0 (Windows 8 R2) |
| 165 | * 4 . 0 (Windows 10) | 165 | * 4 . 0 (Windows 10) |
| 166 | * 5 . 0 (Newer Windows 10) | ||
| 166 | */ | 167 | */ |
| 167 | 168 | ||
| 168 | #define VERSION_WS2008 ((0 << 16) | (13)) | 169 | #define VERSION_WS2008 ((0 << 16) | (13)) |
| @@ -170,10 +171,11 @@ static inline u32 hv_get_bytes_to_write(const struct hv_ring_buffer_info *rbi) | |||
| 170 | #define VERSION_WIN8 ((2 << 16) | (4)) | 171 | #define VERSION_WIN8 ((2 << 16) | (4)) |
| 171 | #define VERSION_WIN8_1 ((3 << 16) | (0)) | 172 | #define VERSION_WIN8_1 ((3 << 16) | (0)) |
| 172 | #define VERSION_WIN10 ((4 << 16) | (0)) | 173 | #define VERSION_WIN10 ((4 << 16) | (0)) |
| 174 | #define VERSION_WIN10_V5 ((5 << 16) | (0)) | ||
| 173 | 175 | ||
| 174 | #define VERSION_INVAL -1 | 176 | #define VERSION_INVAL -1 |
| 175 | 177 | ||
| 176 | #define VERSION_CURRENT VERSION_WIN10 | 178 | #define VERSION_CURRENT VERSION_WIN10_V5 |
| 177 | 179 | ||
| 178 | /* Make maximum size of pipe payload of 16K */ | 180 | /* Make maximum size of pipe payload of 16K */ |
| 179 | #define MAX_PIPE_DATA_PAYLOAD (sizeof(u8) * 16384) | 181 | #define MAX_PIPE_DATA_PAYLOAD (sizeof(u8) * 16384) |
| @@ -570,7 +572,14 @@ struct vmbus_channel_initiate_contact { | |||
| 570 | struct vmbus_channel_message_header header; | 572 | struct vmbus_channel_message_header header; |
| 571 | u32 vmbus_version_requested; | 573 | u32 vmbus_version_requested; |
| 572 | u32 target_vcpu; /* The VCPU the host should respond to */ | 574 | u32 target_vcpu; /* The VCPU the host should respond to */ |
| 573 | u64 interrupt_page; | 575 | union { |
| 576 | u64 interrupt_page; | ||
| 577 | struct { | ||
| 578 | u8 msg_sint; | ||
| 579 | u8 padding1[3]; | ||
| 580 | u32 padding2; | ||
| 581 | }; | ||
| 582 | }; | ||
| 574 | u64 monitor_page1; | 583 | u64 monitor_page1; |
| 575 | u64 monitor_page2; | 584 | u64 monitor_page2; |
| 576 | } __packed; | 585 | } __packed; |
| @@ -585,6 +594,19 @@ struct vmbus_channel_tl_connect_request { | |||
| 585 | struct vmbus_channel_version_response { | 594 | struct vmbus_channel_version_response { |
| 586 | struct vmbus_channel_message_header header; | 595 | struct vmbus_channel_message_header header; |
| 587 | u8 version_supported; | 596 | u8 version_supported; |
| 597 | |||
| 598 | u8 connection_state; | ||
| 599 | u16 padding; | ||
| 600 | |||
| 601 | /* | ||
| 602 | * On new hosts that support VMBus protocol 5.0, we must use | ||
| 603 | * VMBUS_MESSAGE_CONNECTION_ID_4 for the Initiate Contact Message, | ||
| 604 | * and for subsequent messages, we must use the Message Connection ID | ||
| 605 | * field in the host-returned Version Response Message. | ||
| 606 | * | ||
| 607 | * On old hosts, we should always use VMBUS_MESSAGE_CONNECTION_ID (1). | ||
| 608 | */ | ||
| 609 | u32 msg_conn_id; | ||
| 588 | } __packed; | 610 | } __packed; |
| 589 | 611 | ||
| 590 | enum vmbus_channel_state { | 612 | enum vmbus_channel_state { |
diff --git a/include/linux/nubus.h b/include/linux/nubus.h index 6e8200215321..eba50b057f6f 100644 --- a/include/linux/nubus.h +++ b/include/linux/nubus.h | |||
| @@ -163,7 +163,7 @@ void nubus_seq_write_rsrc_mem(struct seq_file *m, | |||
| 163 | unsigned char *nubus_dirptr(const struct nubus_dirent *nd); | 163 | unsigned char *nubus_dirptr(const struct nubus_dirent *nd); |
| 164 | 164 | ||
| 165 | /* Declarations relating to driver model objects */ | 165 | /* Declarations relating to driver model objects */ |
| 166 | int nubus_bus_register(void); | 166 | int nubus_parent_device_register(void); |
| 167 | int nubus_device_register(struct nubus_board *board); | 167 | int nubus_device_register(struct nubus_board *board); |
| 168 | int nubus_driver_register(struct nubus_driver *ndrv); | 168 | int nubus_driver_register(struct nubus_driver *ndrv); |
| 169 | void nubus_driver_unregister(struct nubus_driver *ndrv); | 169 | void nubus_driver_unregister(struct nubus_driver *ndrv); |
diff --git a/include/linux/nvmem-provider.h b/include/linux/nvmem-provider.h index f89598bc4e1c..24def6ad09bb 100644 --- a/include/linux/nvmem-provider.h +++ b/include/linux/nvmem-provider.h | |||
| @@ -77,6 +77,9 @@ struct nvmem_device *devm_nvmem_register(struct device *dev, | |||
| 77 | 77 | ||
| 78 | int devm_nvmem_unregister(struct device *dev, struct nvmem_device *nvmem); | 78 | int devm_nvmem_unregister(struct device *dev, struct nvmem_device *nvmem); |
| 79 | 79 | ||
| 80 | int nvmem_add_cells(struct nvmem_device *nvmem, | ||
| 81 | const struct nvmem_cell_info *info, | ||
| 82 | int ncells); | ||
| 80 | #else | 83 | #else |
| 81 | 84 | ||
| 82 | static inline struct nvmem_device *nvmem_register(const struct nvmem_config *c) | 85 | static inline struct nvmem_device *nvmem_register(const struct nvmem_config *c) |
| @@ -99,6 +102,14 @@ static inline int | |||
| 99 | devm_nvmem_unregister(struct device *dev, struct nvmem_device *nvmem) | 102 | devm_nvmem_unregister(struct device *dev, struct nvmem_device *nvmem) |
| 100 | { | 103 | { |
| 101 | return nvmem_unregister(nvmem); | 104 | return nvmem_unregister(nvmem); |
| 105 | |||
| 106 | } | ||
| 107 | |||
| 108 | static inline int nvmem_add_cells(struct nvmem_device *nvmem, | ||
| 109 | const struct nvmem_cell_info *info, | ||
| 110 | int ncells) | ||
| 111 | { | ||
| 112 | return -ENOSYS; | ||
| 102 | } | 113 | } |
| 103 | 114 | ||
| 104 | #endif /* CONFIG_NVMEM */ | 115 | #endif /* CONFIG_NVMEM */ |
diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h index e91fdcf41049..962971e6a9c7 100644 --- a/include/linux/soundwire/sdw.h +++ b/include/linux/soundwire/sdw.h | |||
| @@ -23,9 +23,24 @@ struct sdw_slave; | |||
| 23 | #define SDW_MASTER_DEV_NUM 14 | 23 | #define SDW_MASTER_DEV_NUM 14 |
| 24 | 24 | ||
| 25 | #define SDW_NUM_DEV_ID_REGISTERS 6 | 25 | #define SDW_NUM_DEV_ID_REGISTERS 6 |
| 26 | /* frame shape defines */ | ||
| 26 | 27 | ||
| 28 | /* | ||
| 29 | * Note: The maximum row define in SoundWire spec 1.1 is 23. In order to | ||
| 30 | * fill hole with 0, one more dummy entry is added | ||
| 31 | */ | ||
| 32 | #define SDW_FRAME_ROWS 24 | ||
| 33 | #define SDW_FRAME_COLS 8 | ||
| 34 | #define SDW_FRAME_ROW_COLS (SDW_FRAME_ROWS * SDW_FRAME_COLS) | ||
| 35 | |||
| 36 | #define SDW_FRAME_CTRL_BITS 48 | ||
| 27 | #define SDW_MAX_DEVICES 11 | 37 | #define SDW_MAX_DEVICES 11 |
| 28 | 38 | ||
| 39 | #define SDW_VALID_PORT_RANGE(n) (n <= 14 && n >= 1) | ||
| 40 | |||
| 41 | #define SDW_DAI_ID_RANGE_START 100 | ||
| 42 | #define SDW_DAI_ID_RANGE_END 200 | ||
| 43 | |||
| 29 | /** | 44 | /** |
| 30 | * enum sdw_slave_status - Slave status | 45 | * enum sdw_slave_status - Slave status |
| 31 | * @SDW_SLAVE_UNATTACHED: Slave is not attached with the bus. | 46 | * @SDW_SLAVE_UNATTACHED: Slave is not attached with the bus. |
| @@ -61,6 +76,30 @@ enum sdw_command_response { | |||
| 61 | SDW_CMD_FAIL_OTHER = 4, | 76 | SDW_CMD_FAIL_OTHER = 4, |
| 62 | }; | 77 | }; |
| 63 | 78 | ||
| 79 | /** | ||
| 80 | * enum sdw_stream_type: data stream type | ||
| 81 | * | ||
| 82 | * @SDW_STREAM_PCM: PCM data stream | ||
| 83 | * @SDW_STREAM_PDM: PDM data stream | ||
| 84 | * | ||
| 85 | * spec doesn't define this, but is used in implementation | ||
| 86 | */ | ||
| 87 | enum sdw_stream_type { | ||
| 88 | SDW_STREAM_PCM = 0, | ||
| 89 | SDW_STREAM_PDM = 1, | ||
| 90 | }; | ||
| 91 | |||
| 92 | /** | ||
| 93 | * enum sdw_data_direction: Data direction | ||
| 94 | * | ||
| 95 | * @SDW_DATA_DIR_RX: Data into Port | ||
| 96 | * @SDW_DATA_DIR_TX: Data out of Port | ||
| 97 | */ | ||
| 98 | enum sdw_data_direction { | ||
| 99 | SDW_DATA_DIR_RX = 0, | ||
| 100 | SDW_DATA_DIR_TX = 1, | ||
| 101 | }; | ||
| 102 | |||
| 64 | /* | 103 | /* |
| 65 | * SDW properties, defined in MIPI DisCo spec v1.0 | 104 | * SDW properties, defined in MIPI DisCo spec v1.0 |
| 66 | */ | 105 | */ |
| @@ -341,11 +380,92 @@ struct sdw_slave_intr_status { | |||
| 341 | }; | 380 | }; |
| 342 | 381 | ||
| 343 | /** | 382 | /** |
| 344 | * struct sdw_slave_ops - Slave driver callback ops | 383 | * sdw_reg_bank - SoundWire register banks |
| 384 | * @SDW_BANK0: Soundwire register bank 0 | ||
| 385 | * @SDW_BANK1: Soundwire register bank 1 | ||
| 386 | */ | ||
| 387 | enum sdw_reg_bank { | ||
| 388 | SDW_BANK0, | ||
| 389 | SDW_BANK1, | ||
| 390 | }; | ||
| 391 | |||
| 392 | /** | ||
| 393 | * struct sdw_bus_conf: Bus configuration | ||
| 394 | * | ||
| 395 | * @clk_freq: Clock frequency, in Hz | ||
| 396 | * @num_rows: Number of rows in frame | ||
| 397 | * @num_cols: Number of columns in frame | ||
| 398 | * @bank: Next register bank | ||
| 399 | */ | ||
| 400 | struct sdw_bus_conf { | ||
| 401 | unsigned int clk_freq; | ||
| 402 | unsigned int num_rows; | ||
| 403 | unsigned int num_cols; | ||
| 404 | unsigned int bank; | ||
| 405 | }; | ||
| 406 | |||
| 407 | /** | ||
| 408 | * struct sdw_prepare_ch: Prepare/De-prepare Data Port channel | ||
| 409 | * | ||
| 410 | * @num: Port number | ||
| 411 | * @ch_mask: Active channel mask | ||
| 412 | * @prepare: Prepare (true) /de-prepare (false) channel | ||
| 413 | * @bank: Register bank, which bank Slave/Master driver should program for | ||
| 414 | * implementation defined registers. This is always updated to next_bank | ||
| 415 | * value read from bus params. | ||
| 416 | * | ||
| 417 | */ | ||
| 418 | struct sdw_prepare_ch { | ||
| 419 | unsigned int num; | ||
| 420 | unsigned int ch_mask; | ||
| 421 | bool prepare; | ||
| 422 | unsigned int bank; | ||
| 423 | }; | ||
| 424 | |||
| 425 | /** | ||
| 426 | * enum sdw_port_prep_ops: Prepare operations for Data Port | ||
| 427 | * | ||
| 428 | * @SDW_OPS_PORT_PRE_PREP: Pre prepare operation for the Port | ||
| 429 | * @SDW_OPS_PORT_PREP: Prepare operation for the Port | ||
| 430 | * @SDW_OPS_PORT_POST_PREP: Post prepare operation for the Port | ||
| 431 | */ | ||
| 432 | enum sdw_port_prep_ops { | ||
| 433 | SDW_OPS_PORT_PRE_PREP = 0, | ||
| 434 | SDW_OPS_PORT_PREP = 1, | ||
| 435 | SDW_OPS_PORT_POST_PREP = 2, | ||
| 436 | }; | ||
| 437 | |||
| 438 | /** | ||
| 439 | * struct sdw_bus_params: Structure holding bus configuration | ||
| 440 | * | ||
| 441 | * @curr_bank: Current bank in use (BANK0/BANK1) | ||
| 442 | * @next_bank: Next bank to use (BANK0/BANK1). next_bank will always be | ||
| 443 | * set to !curr_bank | ||
| 444 | * @max_dr_freq: Maximum double rate clock frequency supported, in Hz | ||
| 445 | * @curr_dr_freq: Current double rate clock frequency, in Hz | ||
| 446 | * @bandwidth: Current bandwidth | ||
| 447 | * @col: Active columns | ||
| 448 | * @row: Active rows | ||
| 449 | */ | ||
| 450 | struct sdw_bus_params { | ||
| 451 | enum sdw_reg_bank curr_bank; | ||
| 452 | enum sdw_reg_bank next_bank; | ||
| 453 | unsigned int max_dr_freq; | ||
| 454 | unsigned int curr_dr_freq; | ||
| 455 | unsigned int bandwidth; | ||
| 456 | unsigned int col; | ||
| 457 | unsigned int row; | ||
| 458 | }; | ||
| 459 | |||
| 460 | /** | ||
| 461 | * struct sdw_slave_ops: Slave driver callback ops | ||
| 462 | * | ||
| 345 | * @read_prop: Read Slave properties | 463 | * @read_prop: Read Slave properties |
| 346 | * @interrupt_callback: Device interrupt notification (invoked in thread | 464 | * @interrupt_callback: Device interrupt notification (invoked in thread |
| 347 | * context) | 465 | * context) |
| 348 | * @update_status: Update Slave status | 466 | * @update_status: Update Slave status |
| 467 | * @bus_config: Update the bus config for Slave | ||
| 468 | * @port_prep: Prepare the port with parameters | ||
| 349 | */ | 469 | */ |
| 350 | struct sdw_slave_ops { | 470 | struct sdw_slave_ops { |
| 351 | int (*read_prop)(struct sdw_slave *sdw); | 471 | int (*read_prop)(struct sdw_slave *sdw); |
| @@ -353,6 +473,11 @@ struct sdw_slave_ops { | |||
| 353 | struct sdw_slave_intr_status *status); | 473 | struct sdw_slave_intr_status *status); |
| 354 | int (*update_status)(struct sdw_slave *slave, | 474 | int (*update_status)(struct sdw_slave *slave, |
| 355 | enum sdw_slave_status status); | 475 | enum sdw_slave_status status); |
| 476 | int (*bus_config)(struct sdw_slave *slave, | ||
| 477 | struct sdw_bus_params *params); | ||
| 478 | int (*port_prep)(struct sdw_slave *slave, | ||
| 479 | struct sdw_prepare_ch *prepare_ch, | ||
| 480 | enum sdw_port_prep_ops pre_ops); | ||
| 356 | }; | 481 | }; |
| 357 | 482 | ||
| 358 | /** | 483 | /** |
| @@ -406,6 +531,93 @@ int sdw_handle_slave_status(struct sdw_bus *bus, | |||
| 406 | * SDW master structures and APIs | 531 | * SDW master structures and APIs |
| 407 | */ | 532 | */ |
| 408 | 533 | ||
| 534 | /** | ||
| 535 | * struct sdw_port_params: Data Port parameters | ||
| 536 | * | ||
| 537 | * @num: Port number | ||
| 538 | * @bps: Word length of the Port | ||
| 539 | * @flow_mode: Port Data flow mode | ||
| 540 | * @data_mode: Test modes or normal mode | ||
| 541 | * | ||
| 542 | * This is used to program the Data Port based on Data Port stream | ||
| 543 | * parameters. | ||
| 544 | */ | ||
| 545 | struct sdw_port_params { | ||
| 546 | unsigned int num; | ||
| 547 | unsigned int bps; | ||
| 548 | unsigned int flow_mode; | ||
| 549 | unsigned int data_mode; | ||
| 550 | }; | ||
| 551 | |||
| 552 | /** | ||
| 553 | * struct sdw_transport_params: Data Port Transport Parameters | ||
| 554 | * | ||
| 555 | * @blk_grp_ctrl_valid: Port implements block group control | ||
| 556 | * @num: Port number | ||
| 557 | * @blk_grp_ctrl: Block group control value | ||
| 558 | * @sample_interval: Sample interval | ||
| 559 | * @offset1: Blockoffset of the payload data | ||
| 560 | * @offset2: Blockoffset of the payload data | ||
| 561 | * @hstart: Horizontal start of the payload data | ||
| 562 | * @hstop: Horizontal stop of the payload data | ||
| 563 | * @blk_pkg_mode: Block per channel or block per port | ||
| 564 | * @lane_ctrl: Data lane Port uses for Data transfer. Currently only single | ||
| 565 | * data lane is supported in bus | ||
| 566 | * | ||
| 567 | * This is used to program the Data Port based on Data Port transport | ||
| 568 | * parameters. All these parameters are banked and can be modified | ||
| 569 | * during a bank switch without any artifacts in audio stream. | ||
| 570 | */ | ||
| 571 | struct sdw_transport_params { | ||
| 572 | bool blk_grp_ctrl_valid; | ||
| 573 | unsigned int port_num; | ||
| 574 | unsigned int blk_grp_ctrl; | ||
| 575 | unsigned int sample_interval; | ||
| 576 | unsigned int offset1; | ||
| 577 | unsigned int offset2; | ||
| 578 | unsigned int hstart; | ||
| 579 | unsigned int hstop; | ||
| 580 | unsigned int blk_pkg_mode; | ||
| 581 | unsigned int lane_ctrl; | ||
| 582 | }; | ||
| 583 | |||
| 584 | /** | ||
| 585 | * struct sdw_enable_ch: Enable/disable Data Port channel | ||
| 586 | * | ||
| 587 | * @num: Port number | ||
| 588 | * @ch_mask: Active channel mask | ||
| 589 | * @enable: Enable (true) /disable (false) channel | ||
| 590 | */ | ||
| 591 | struct sdw_enable_ch { | ||
| 592 | unsigned int port_num; | ||
| 593 | unsigned int ch_mask; | ||
| 594 | bool enable; | ||
| 595 | }; | ||
| 596 | |||
| 597 | /** | ||
| 598 | * struct sdw_master_port_ops: Callback functions from bus to Master | ||
| 599 | * driver to set Master Data ports. | ||
| 600 | * | ||
| 601 | * @dpn_set_port_params: Set the Port parameters for the Master Port. | ||
| 602 | * Mandatory callback | ||
| 603 | * @dpn_set_port_transport_params: Set transport parameters for the Master | ||
| 604 | * Port. Mandatory callback | ||
| 605 | * @dpn_port_prep: Port prepare operations for the Master Data Port. | ||
| 606 | * @dpn_port_enable_ch: Enable the channels of Master Port. | ||
| 607 | */ | ||
| 608 | struct sdw_master_port_ops { | ||
| 609 | int (*dpn_set_port_params)(struct sdw_bus *bus, | ||
| 610 | struct sdw_port_params *port_params, | ||
| 611 | unsigned int bank); | ||
| 612 | int (*dpn_set_port_transport_params)(struct sdw_bus *bus, | ||
| 613 | struct sdw_transport_params *transport_params, | ||
| 614 | enum sdw_reg_bank bank); | ||
| 615 | int (*dpn_port_prep)(struct sdw_bus *bus, | ||
| 616 | struct sdw_prepare_ch *prepare_ch); | ||
| 617 | int (*dpn_port_enable_ch)(struct sdw_bus *bus, | ||
| 618 | struct sdw_enable_ch *enable_ch, unsigned int bank); | ||
| 619 | }; | ||
| 620 | |||
| 409 | struct sdw_msg; | 621 | struct sdw_msg; |
| 410 | 622 | ||
| 411 | /** | 623 | /** |
| @@ -426,6 +638,9 @@ struct sdw_defer { | |||
| 426 | * @xfer_msg: Transfer message callback | 638 | * @xfer_msg: Transfer message callback |
| 427 | * @xfer_msg_defer: Defer version of transfer message callback | 639 | * @xfer_msg_defer: Defer version of transfer message callback |
| 428 | * @reset_page_addr: Reset the SCP page address registers | 640 | * @reset_page_addr: Reset the SCP page address registers |
| 641 | * @set_bus_conf: Set the bus configuration | ||
| 642 | * @pre_bank_switch: Callback for pre bank switch | ||
| 643 | * @post_bank_switch: Callback for post bank switch | ||
| 429 | */ | 644 | */ |
| 430 | struct sdw_master_ops { | 645 | struct sdw_master_ops { |
| 431 | int (*read_prop)(struct sdw_bus *bus); | 646 | int (*read_prop)(struct sdw_bus *bus); |
| @@ -437,6 +652,11 @@ struct sdw_master_ops { | |||
| 437 | struct sdw_defer *defer); | 652 | struct sdw_defer *defer); |
| 438 | enum sdw_command_response (*reset_page_addr) | 653 | enum sdw_command_response (*reset_page_addr) |
| 439 | (struct sdw_bus *bus, unsigned int dev_num); | 654 | (struct sdw_bus *bus, unsigned int dev_num); |
| 655 | int (*set_bus_conf)(struct sdw_bus *bus, | ||
| 656 | struct sdw_bus_params *params); | ||
| 657 | int (*pre_bank_switch)(struct sdw_bus *bus); | ||
| 658 | int (*post_bank_switch)(struct sdw_bus *bus); | ||
| 659 | |||
| 440 | }; | 660 | }; |
| 441 | 661 | ||
| 442 | /** | 662 | /** |
| @@ -449,9 +669,15 @@ struct sdw_master_ops { | |||
| 449 | * @bus_lock: bus lock | 669 | * @bus_lock: bus lock |
| 450 | * @msg_lock: message lock | 670 | * @msg_lock: message lock |
| 451 | * @ops: Master callback ops | 671 | * @ops: Master callback ops |
| 672 | * @port_ops: Master port callback ops | ||
| 673 | * @params: Current bus parameters | ||
| 452 | * @prop: Master properties | 674 | * @prop: Master properties |
| 675 | * @m_rt_list: List of Master instance of all stream(s) running on Bus. This | ||
| 676 | * is used to compute and program bus bandwidth, clock, frame shape, | ||
| 677 | * transport and port parameters | ||
| 453 | * @defer_msg: Defer message | 678 | * @defer_msg: Defer message |
| 454 | * @clk_stop_timeout: Clock stop timeout computed | 679 | * @clk_stop_timeout: Clock stop timeout computed |
| 680 | * @bank_switch_timeout: Bank switch timeout computed | ||
| 455 | */ | 681 | */ |
| 456 | struct sdw_bus { | 682 | struct sdw_bus { |
| 457 | struct device *dev; | 683 | struct device *dev; |
| @@ -461,14 +687,118 @@ struct sdw_bus { | |||
| 461 | struct mutex bus_lock; | 687 | struct mutex bus_lock; |
| 462 | struct mutex msg_lock; | 688 | struct mutex msg_lock; |
| 463 | const struct sdw_master_ops *ops; | 689 | const struct sdw_master_ops *ops; |
| 690 | const struct sdw_master_port_ops *port_ops; | ||
| 691 | struct sdw_bus_params params; | ||
| 464 | struct sdw_master_prop prop; | 692 | struct sdw_master_prop prop; |
| 693 | struct list_head m_rt_list; | ||
| 465 | struct sdw_defer defer_msg; | 694 | struct sdw_defer defer_msg; |
| 466 | unsigned int clk_stop_timeout; | 695 | unsigned int clk_stop_timeout; |
| 696 | u32 bank_switch_timeout; | ||
| 467 | }; | 697 | }; |
| 468 | 698 | ||
| 469 | int sdw_add_bus_master(struct sdw_bus *bus); | 699 | int sdw_add_bus_master(struct sdw_bus *bus); |
| 470 | void sdw_delete_bus_master(struct sdw_bus *bus); | 700 | void sdw_delete_bus_master(struct sdw_bus *bus); |
| 471 | 701 | ||
| 702 | /** | ||
| 703 | * sdw_port_config: Master or Slave Port configuration | ||
| 704 | * | ||
| 705 | * @num: Port number | ||
| 706 | * @ch_mask: channels mask for port | ||
| 707 | */ | ||
| 708 | struct sdw_port_config { | ||
| 709 | unsigned int num; | ||
| 710 | unsigned int ch_mask; | ||
| 711 | }; | ||
| 712 | |||
| 713 | /** | ||
| 714 | * sdw_stream_config: Master or Slave stream configuration | ||
| 715 | * | ||
| 716 | * @frame_rate: Audio frame rate of the stream, in Hz | ||
| 717 | * @ch_count: Channel count of the stream | ||
| 718 | * @bps: Number of bits per audio sample | ||
| 719 | * @direction: Data direction | ||
| 720 | * @type: Stream type PCM or PDM | ||
| 721 | */ | ||
| 722 | struct sdw_stream_config { | ||
| 723 | unsigned int frame_rate; | ||
| 724 | unsigned int ch_count; | ||
| 725 | unsigned int bps; | ||
| 726 | enum sdw_data_direction direction; | ||
| 727 | enum sdw_stream_type type; | ||
| 728 | }; | ||
| 729 | |||
| 730 | /** | ||
| 731 | * sdw_stream_state: Stream states | ||
| 732 | * | ||
| 733 | * @SDW_STREAM_ALLOCATED: New stream allocated. | ||
| 734 | * @SDW_STREAM_CONFIGURED: Stream configured | ||
| 735 | * @SDW_STREAM_PREPARED: Stream prepared | ||
| 736 | * @SDW_STREAM_ENABLED: Stream enabled | ||
| 737 | * @SDW_STREAM_DISABLED: Stream disabled | ||
| 738 | * @SDW_STREAM_DEPREPARED: Stream de-prepared | ||
| 739 | * @SDW_STREAM_RELEASED: Stream released | ||
| 740 | */ | ||
| 741 | enum sdw_stream_state { | ||
| 742 | SDW_STREAM_ALLOCATED = 0, | ||
| 743 | SDW_STREAM_CONFIGURED = 1, | ||
| 744 | SDW_STREAM_PREPARED = 2, | ||
| 745 | SDW_STREAM_ENABLED = 3, | ||
| 746 | SDW_STREAM_DISABLED = 4, | ||
| 747 | SDW_STREAM_DEPREPARED = 5, | ||
| 748 | SDW_STREAM_RELEASED = 6, | ||
| 749 | }; | ||
| 750 | |||
| 751 | /** | ||
| 752 | * sdw_stream_params: Stream parameters | ||
| 753 | * | ||
| 754 | * @rate: Sampling frequency, in Hz | ||
| 755 | * @ch_count: Number of channels | ||
| 756 | * @bps: bits per channel sample | ||
| 757 | */ | ||
| 758 | struct sdw_stream_params { | ||
| 759 | unsigned int rate; | ||
| 760 | unsigned int ch_count; | ||
| 761 | unsigned int bps; | ||
| 762 | }; | ||
| 763 | |||
| 764 | /** | ||
| 765 | * sdw_stream_runtime: Runtime stream parameters | ||
| 766 | * | ||
| 767 | * @name: SoundWire stream name | ||
| 768 | * @params: Stream parameters | ||
| 769 | * @state: Current state of the stream | ||
| 770 | * @type: Stream type PCM or PDM | ||
| 771 | * @m_rt: Master runtime | ||
| 772 | */ | ||
| 773 | struct sdw_stream_runtime { | ||
| 774 | char *name; | ||
| 775 | struct sdw_stream_params params; | ||
| 776 | enum sdw_stream_state state; | ||
| 777 | enum sdw_stream_type type; | ||
| 778 | struct sdw_master_runtime *m_rt; | ||
| 779 | }; | ||
| 780 | |||
| 781 | struct sdw_stream_runtime *sdw_alloc_stream(char *stream_name); | ||
| 782 | void sdw_release_stream(struct sdw_stream_runtime *stream); | ||
| 783 | int sdw_stream_add_master(struct sdw_bus *bus, | ||
| 784 | struct sdw_stream_config *stream_config, | ||
| 785 | struct sdw_port_config *port_config, | ||
| 786 | unsigned int num_ports, | ||
| 787 | struct sdw_stream_runtime *stream); | ||
| 788 | int sdw_stream_add_slave(struct sdw_slave *slave, | ||
| 789 | struct sdw_stream_config *stream_config, | ||
| 790 | struct sdw_port_config *port_config, | ||
| 791 | unsigned int num_ports, | ||
| 792 | struct sdw_stream_runtime *stream); | ||
| 793 | int sdw_stream_remove_master(struct sdw_bus *bus, | ||
| 794 | struct sdw_stream_runtime *stream); | ||
| 795 | int sdw_stream_remove_slave(struct sdw_slave *slave, | ||
| 796 | struct sdw_stream_runtime *stream); | ||
| 797 | int sdw_prepare_stream(struct sdw_stream_runtime *stream); | ||
| 798 | int sdw_enable_stream(struct sdw_stream_runtime *stream); | ||
| 799 | int sdw_disable_stream(struct sdw_stream_runtime *stream); | ||
| 800 | int sdw_deprepare_stream(struct sdw_stream_runtime *stream); | ||
| 801 | |||
| 472 | /* messaging and data APIs */ | 802 | /* messaging and data APIs */ |
| 473 | 803 | ||
| 474 | int sdw_read(struct sdw_slave *slave, u32 addr); | 804 | int sdw_read(struct sdw_slave *slave, u32 addr); |
diff --git a/include/linux/soundwire/sdw_intel.h b/include/linux/soundwire/sdw_intel.h index 4b37528f592d..2b9573b8aedd 100644 --- a/include/linux/soundwire/sdw_intel.h +++ b/include/linux/soundwire/sdw_intel.h | |||
| @@ -5,17 +5,31 @@ | |||
| 5 | #define __SDW_INTEL_H | 5 | #define __SDW_INTEL_H |
| 6 | 6 | ||
| 7 | /** | 7 | /** |
| 8 | * struct sdw_intel_ops: Intel audio driver callback ops | ||
| 9 | * | ||
| 10 | * @config_stream: configure the stream with the hw_params | ||
| 11 | */ | ||
| 12 | struct sdw_intel_ops { | ||
| 13 | int (*config_stream)(void *arg, void *substream, | ||
| 14 | void *dai, void *hw_params, int stream_num); | ||
| 15 | }; | ||
| 16 | |||
| 17 | /** | ||
| 8 | * struct sdw_intel_res - Soundwire Intel resource structure | 18 | * struct sdw_intel_res - Soundwire Intel resource structure |
| 9 | * @mmio_base: mmio base of SoundWire registers | 19 | * @mmio_base: mmio base of SoundWire registers |
| 10 | * @irq: interrupt number | 20 | * @irq: interrupt number |
| 11 | * @handle: ACPI parent handle | 21 | * @handle: ACPI parent handle |
| 12 | * @parent: parent device | 22 | * @parent: parent device |
| 23 | * @ops: callback ops | ||
| 24 | * @arg: callback arg | ||
| 13 | */ | 25 | */ |
| 14 | struct sdw_intel_res { | 26 | struct sdw_intel_res { |
| 15 | void __iomem *mmio_base; | 27 | void __iomem *mmio_base; |
| 16 | int irq; | 28 | int irq; |
| 17 | acpi_handle handle; | 29 | acpi_handle handle; |
| 18 | struct device *parent; | 30 | struct device *parent; |
| 31 | const struct sdw_intel_ops *ops; | ||
| 32 | void *arg; | ||
| 19 | }; | 33 | }; |
| 20 | 34 | ||
| 21 | void *sdw_intel_init(acpi_handle *parent_handle, struct sdw_intel_res *res); | 35 | void *sdw_intel_init(acpi_handle *parent_handle, struct sdw_intel_res *res); |
diff --git a/include/linux/uio_driver.h b/include/linux/uio_driver.h index 3c85c81b0027..6c5f2074e14f 100644 --- a/include/linux/uio_driver.h +++ b/include/linux/uio_driver.h | |||
| @@ -14,6 +14,7 @@ | |||
| 14 | #ifndef _UIO_DRIVER_H_ | 14 | #ifndef _UIO_DRIVER_H_ |
| 15 | #define _UIO_DRIVER_H_ | 15 | #define _UIO_DRIVER_H_ |
| 16 | 16 | ||
| 17 | #include <linux/device.h> | ||
| 17 | #include <linux/fs.h> | 18 | #include <linux/fs.h> |
| 18 | #include <linux/interrupt.h> | 19 | #include <linux/interrupt.h> |
| 19 | 20 | ||
| @@ -68,12 +69,13 @@ struct uio_port { | |||
| 68 | 69 | ||
| 69 | struct uio_device { | 70 | struct uio_device { |
| 70 | struct module *owner; | 71 | struct module *owner; |
| 71 | struct device *dev; | 72 | struct device dev; |
| 72 | int minor; | 73 | int minor; |
| 73 | atomic_t event; | 74 | atomic_t event; |
| 74 | struct fasync_struct *async_queue; | 75 | struct fasync_struct *async_queue; |
| 75 | wait_queue_head_t wait; | 76 | wait_queue_head_t wait; |
| 76 | struct uio_info *info; | 77 | struct uio_info *info; |
| 78 | spinlock_t info_lock; | ||
| 77 | struct kobject *map_dir; | 79 | struct kobject *map_dir; |
| 78 | struct kobject *portio_dir; | 80 | struct kobject *portio_dir; |
| 79 | }; | 81 | }; |
diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h index 8ad11669e4d8..3ddb575eed54 100644 --- a/include/sound/soc-dai.h +++ b/include/sound/soc-dai.h | |||
| @@ -170,6 +170,8 @@ struct snd_soc_dai_ops { | |||
| 170 | unsigned int rx_num, unsigned int *rx_slot); | 170 | unsigned int rx_num, unsigned int *rx_slot); |
| 171 | int (*set_tristate)(struct snd_soc_dai *dai, int tristate); | 171 | int (*set_tristate)(struct snd_soc_dai *dai, int tristate); |
| 172 | 172 | ||
| 173 | int (*set_sdw_stream)(struct snd_soc_dai *dai, | ||
| 174 | void *stream, int direction); | ||
| 173 | /* | 175 | /* |
| 174 | * DAI digital mute - optional. | 176 | * DAI digital mute - optional. |
| 175 | * Called by soc-core to minimise any pops. | 177 | * Called by soc-core to minimise any pops. |
| @@ -358,4 +360,25 @@ static inline void *snd_soc_dai_get_drvdata(struct snd_soc_dai *dai) | |||
| 358 | return dev_get_drvdata(dai->dev); | 360 | return dev_get_drvdata(dai->dev); |
| 359 | } | 361 | } |
| 360 | 362 | ||
| 363 | /** | ||
| 364 | * snd_soc_dai_set_sdw_stream() - Configures a DAI for SDW stream operation | ||
| 365 | * @dai: DAI | ||
| 366 | * @stream: STREAM | ||
| 367 | * @direction: Stream direction(Playback/Capture) | ||
| 368 | * SoundWire subsystem doesn't have a notion of direction and we reuse | ||
| 369 | * the ASoC stream direction to configure sink/source ports. | ||
| 370 | * Playback maps to source ports and Capture for sink ports. | ||
| 371 | * | ||
| 372 | * This should be invoked with NULL to clear the stream set previously. | ||
| 373 | * Returns 0 on success, a negative error code otherwise. | ||
| 374 | */ | ||
| 375 | static inline int snd_soc_dai_set_sdw_stream(struct snd_soc_dai *dai, | ||
| 376 | void *stream, int direction) | ||
| 377 | { | ||
| 378 | if (dai->driver->ops->set_sdw_stream) | ||
| 379 | return dai->driver->ops->set_sdw_stream(dai, stream, direction); | ||
| 380 | else | ||
| 381 | return -ENOTSUPP; | ||
| 382 | } | ||
| 383 | |||
| 361 | #endif | 384 | #endif |
diff --git a/scripts/mod/devicetable-offsets.c b/scripts/mod/devicetable-offsets.c index 9fad6afe4c41..6667f7b491d6 100644 --- a/scripts/mod/devicetable-offsets.c +++ b/scripts/mod/devicetable-offsets.c | |||
| @@ -139,6 +139,9 @@ int main(void) | |||
| 139 | DEVID(hv_vmbus_device_id); | 139 | DEVID(hv_vmbus_device_id); |
| 140 | DEVID_FIELD(hv_vmbus_device_id, guid); | 140 | DEVID_FIELD(hv_vmbus_device_id, guid); |
| 141 | 141 | ||
| 142 | DEVID(rpmsg_device_id); | ||
| 143 | DEVID_FIELD(rpmsg_device_id, name); | ||
| 144 | |||
| 142 | DEVID(i2c_device_id); | 145 | DEVID(i2c_device_id); |
| 143 | DEVID_FIELD(i2c_device_id, name); | 146 | DEVID_FIELD(i2c_device_id, name); |
| 144 | 147 | ||
diff --git a/scripts/mod/file2alias.c b/scripts/mod/file2alias.c index b9beeaa4695b..52fd54a8fe39 100644 --- a/scripts/mod/file2alias.c +++ b/scripts/mod/file2alias.c | |||
| @@ -944,6 +944,17 @@ static int do_vmbus_entry(const char *filename, void *symval, | |||
| 944 | } | 944 | } |
| 945 | ADD_TO_DEVTABLE("vmbus", hv_vmbus_device_id, do_vmbus_entry); | 945 | ADD_TO_DEVTABLE("vmbus", hv_vmbus_device_id, do_vmbus_entry); |
| 946 | 946 | ||
| 947 | /* Looks like: rpmsg:S */ | ||
| 948 | static int do_rpmsg_entry(const char *filename, void *symval, | ||
| 949 | char *alias) | ||
| 950 | { | ||
| 951 | DEF_FIELD_ADDR(symval, rpmsg_device_id, name); | ||
| 952 | sprintf(alias, RPMSG_DEVICE_MODALIAS_FMT, *name); | ||
| 953 | |||
| 954 | return 1; | ||
| 955 | } | ||
| 956 | ADD_TO_DEVTABLE("rpmsg", rpmsg_device_id, do_rpmsg_entry); | ||
| 957 | |||
| 947 | /* Looks like: i2c:S */ | 958 | /* Looks like: i2c:S */ |
| 948 | static int do_i2c_entry(const char *filename, void *symval, | 959 | static int do_i2c_entry(const char *filename, void *symval, |
| 949 | char *alias) | 960 | char *alias) |
diff --git a/scripts/ver_linux b/scripts/ver_linux index 545ec7388eb7..7227994ccf63 100755 --- a/scripts/ver_linux +++ b/scripts/ver_linux | |||
| @@ -13,36 +13,34 @@ BEGIN { | |||
| 13 | system("uname -a") | 13 | system("uname -a") |
| 14 | printf("\n") | 14 | printf("\n") |
| 15 | 15 | ||
| 16 | printversion("GNU C", version("gcc -dumpversion 2>&1")) | 16 | printversion("GNU C", version("gcc -dumpversion")) |
| 17 | printversion("GNU Make", version("make --version 2>&1")) | 17 | printversion("GNU Make", version("make --version")) |
| 18 | printversion("Binutils", version("ld -v 2>&1")) | 18 | printversion("Binutils", version("ld -v")) |
| 19 | printversion("Util-linux", version("mount --version 2>&1")) | 19 | printversion("Util-linux", version("mount --version")) |
| 20 | printversion("Mount", version("mount --version 2>&1")) | 20 | printversion("Mount", version("mount --version")) |
| 21 | printversion("Module-init-tools", version("depmod -V 2>&1")) | 21 | printversion("Module-init-tools", version("depmod -V")) |
| 22 | printversion("E2fsprogs", version("tune2fs 2>&1")) | 22 | printversion("E2fsprogs", version("tune2fs")) |
| 23 | printversion("Jfsutils", version("fsck.jfs -V 2>&1")) | 23 | printversion("Jfsutils", version("fsck.jfs -V")) |
| 24 | printversion("Reiserfsprogs", version("reiserfsck -V 2>&1")) | 24 | printversion("Reiserfsprogs", version("reiserfsck -V")) |
| 25 | printversion("Reiser4fsprogs", version("fsck.reiser4 -V 2>&1")) | 25 | printversion("Reiser4fsprogs", version("fsck.reiser4 -V")) |
| 26 | printversion("Xfsprogs", version("xfs_db -V 2>&1")) | 26 | printversion("Xfsprogs", version("xfs_db -V")) |
| 27 | printversion("Pcmciautils", version("pccardctl -V 2>&1")) | 27 | printversion("Pcmciautils", version("pccardctl -V")) |
| 28 | printversion("Pcmcia-cs", version("cardmgr -V 2>&1")) | 28 | printversion("Pcmcia-cs", version("cardmgr -V")) |
| 29 | printversion("Quota-tools", version("quota -V 2>&1")) | 29 | printversion("Quota-tools", version("quota -V")) |
| 30 | printversion("PPP", version("pppd --version 2>&1")) | 30 | printversion("PPP", version("pppd --version")) |
| 31 | printversion("Isdn4k-utils", version("isdnctrl 2>&1")) | 31 | printversion("Isdn4k-utils", version("isdnctrl")) |
| 32 | printversion("Nfs-utils", version("showmount --version 2>&1")) | 32 | printversion("Nfs-utils", version("showmount --version")) |
| 33 | 33 | ||
| 34 | if (system("test -r /proc/self/maps") == 0) { | 34 | while (getline <"/proc/self/maps" > 0) { |
| 35 | while (getline <"/proc/self/maps" > 0) { | 35 | n = split($0, procmaps, "/") |
| 36 | n = split($0, procmaps, "/") | 36 | if (/libc.*so$/ && match(procmaps[n], /[0-9]+([.]?[0-9]+)+/)) { |
| 37 | if (/libc.*so$/ && match(procmaps[n], /[0-9]+([.]?[0-9]+)+/)) { | 37 | ver = substr(procmaps[n], RSTART, RLENGTH) |
| 38 | ver = substr(procmaps[n], RSTART, RLENGTH) | 38 | printversion("Linux C Library", ver) |
| 39 | printversion("Linux C Library", ver) | 39 | break |
| 40 | break | ||
| 41 | } | ||
| 42 | } | 40 | } |
| 43 | } | 41 | } |
| 44 | 42 | ||
| 45 | printversion("Dynamic linker (ldd)", version("ldd --version 2>&1")) | 43 | printversion("Dynamic linker (ldd)", version("ldd --version")) |
| 46 | 44 | ||
| 47 | while ("ldconfig -p 2>/dev/null" | getline > 0) { | 45 | while ("ldconfig -p 2>/dev/null" | getline > 0) { |
| 48 | if (/(libg|stdc)[+]+\.so/) { | 46 | if (/(libg|stdc)[+]+\.so/) { |
| @@ -50,28 +48,25 @@ BEGIN { | |||
| 50 | break | 48 | break |
| 51 | } | 49 | } |
| 52 | } | 50 | } |
| 53 | if (system("test -r " libcpp) == 0) | 51 | printversion("Linux C++ Library", version("readlink " libcpp)) |
| 54 | printversion("Linux C++ Library", version("readlink " libcpp)) | 52 | printversion("Procps", version("ps --version")) |
| 55 | 53 | printversion("Net-tools", version("ifconfig --version")) | |
| 56 | printversion("Procps", version("ps --version 2>&1")) | 54 | printversion("Kbd", version("loadkeys -V")) |
| 57 | printversion("Net-tools", version("ifconfig --version 2>&1")) | 55 | printversion("Console-tools", version("loadkeys -V")) |
| 58 | printversion("Kbd", version("loadkeys -V 2>&1")) | 56 | printversion("Oprofile", version("oprofiled --version")) |
| 59 | printversion("Console-tools", version("loadkeys -V 2>&1")) | 57 | printversion("Sh-utils", version("expr --v")) |
| 60 | printversion("Oprofile", version("oprofiled --version 2>&1")) | 58 | printversion("Udev", version("udevadm --version")) |
| 61 | printversion("Sh-utils", version("expr --v 2>&1")) | 59 | printversion("Wireless-tools", version("iwconfig --version")) |
| 62 | printversion("Udev", version("udevadm --version 2>&1")) | ||
| 63 | printversion("Wireless-tools", version("iwconfig --version 2>&1")) | ||
| 64 | 60 | ||
| 65 | if (system("test -r /proc/modules") == 0) { | 61 | while ("sort /proc/modules" | getline > 0) { |
| 66 | while ("sort /proc/modules" | getline > 0) { | 62 | mods = mods sep $1 |
| 67 | mods = mods sep $1 | 63 | sep = " " |
| 68 | sep = " " | ||
| 69 | } | ||
| 70 | printversion("Modules Loaded", mods) | ||
| 71 | } | 64 | } |
| 65 | printversion("Modules Loaded", mods) | ||
| 72 | } | 66 | } |
| 73 | 67 | ||
| 74 | function version(cmd, ver) { | 68 | function version(cmd, ver) { |
| 69 | cmd = cmd " 2>&1" | ||
| 75 | while (cmd | getline > 0) { | 70 | while (cmd | getline > 0) { |
| 76 | if (!/ver_linux/ && match($0, /[0-9]+([.]?[0-9]+)+/)) { | 71 | if (!/ver_linux/ && match($0, /[0-9]+([.]?[0-9]+)+/)) { |
| 77 | ver = substr($0, RSTART, RLENGTH) | 72 | ver = substr($0, RSTART, RLENGTH) |
