Pull char/misc updates from Greg KH: "Here is the big set of char/misc driver patches for 4.17-rc1. There are a lot of little things in here, nothing huge, but all important to the different hardware types involved: - thunderbolt driver updates - parport updates (people still care...) - nvmem driver updates - mei updates (as always) - hwtracing driver updates - hyperv driver updates - extcon driver updates - ... and a handful of even smaller driver subsystem and individual driver updates All of these have been in linux-next with no reported issues" * tag 'char-misc-4.17-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc: (149 commits) hwtracing: Add HW tracing support menu intel_th: Add ACPI glue layer intel_th: Allow forcing host mode through drvdata intel_th: Pick up irq number from resources intel_th: Don't touch switch routing in host mode intel_th: Use correct method of finding hub intel_th: Add SPDX GPL-2.0 header to replace GPLv2 boilerplate stm class: Make dummy's master/channel ranges configurable stm class: Add SPDX GPL-2.0 header to replace GPLv2 boilerplate MAINTAINERS: Bestow upon myself the care for drivers/hwtracing hv: add SPDX license id to Kconfig hv: add SPDX license to trace Drivers: hv: vmbus: do not mark HV_PCIE as perf_device Drivers: hv: vmbus: respect what we get from hv_get_synint_state() /dev/mem: Avoid overwriting "err" in read_mem() eeprom: at24: use SPDX identifier instead of GPL boiler-plate eeprom: at24: simplify the i2c functionality checking eeprom: at24: fix a line break eeprom: at24: tweak newlines eeprom: at24: refactor at24_probe() ...
Unified · Split
| @@ -132,3 +132,10 @@ KernelVersion: 4.16 · 131 unmodified lines | |||
| 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 | |||
| 136 | What: /sys/bus/vmbus/devices/vmbus_*/channels/NN/ring | ||
| 137 | Date: January. 2018 | ||
| 138 | KernelVersion: 4.16 | ||
| 139 | Contact: Stephen Hemminger <sthemmin@microsoft.com> | ||
| 140 | Description: Binary file created by uio_hv_generic for ring buffer | ||
| 141 | Users: Userspace drivers | ||
| @@ -1,3 +1,26 @@ | |||
| 1 | What: /sys/bus/thunderbolt/devices/.../domainX/boot_acl | ||
| 2 | Date: Jun 2018 | ||
| 3 | KernelVersion: 4.17 | ||
| 4 | Contact: thunderbolt-software@lists.01.org | ||
| 5 | Description: Holds a comma separated list of device unique_ids that | ||
| 6 | are allowed to be connected automatically during system | ||
| 7 | startup (e.g boot devices). The list always contains | ||
| 8 | maximum supported number of unique_ids where unused | ||
| 9 | entries are empty. This allows the userspace software | ||
| 10 | to determine how many entries the controller supports. | ||
| 11 | If there are multiple controllers, each controller has | ||
| 12 | its own ACL list and size may be different between the | ||
| 13 | controllers. | ||
| 14 | |||
| 15 | System BIOS may have an option "Preboot ACL" or similar | ||
| 16 | that needs to be selected before this list is taken into | ||
| 17 | consideration. | ||
| 18 | |||
| 19 | Software always updates a full list in each write. | ||
| 20 | |||
| 21 | If a device is authorized automatically during boot its | ||
| 22 | boot attribute is set to 1. | ||
| 23 | |||
| 1 | What: /sys/bus/thunderbolt/devices/.../domainX/security | 24 | What: /sys/bus/thunderbolt/devices/.../domainX/security |
| 2 | Date: Sep 2017 | 25 | Date: Sep 2017 |
| 3 | KernelVersion: 4.13 | 26 | KernelVersion: 4.13 |
| @@ -12,6 +35,9 @@ Description: This attribute holds current Thunderbolt security level · 8 unmodified lines | |||
| 12 | minimum. User needs to authorize each device. | 35 | minimum. User needs to authorize each device. |
| 13 | dponly: Automatically tunnel Display port (and USB). No | 36 | dponly: Automatically tunnel Display port (and USB). No |
| 14 | PCIe tunnels are created. | 37 | PCIe tunnels are created. |
| 38 | usbonly: Automatically tunnel USB controller of the | ||
| 39 | connected Thunderbolt dock (and Display Port). All | ||
| 40 | PCIe links downstream of the dock are removed. | ||
| 15 | 41 | ||
| 16 | What: /sys/bus/thunderbolt/devices/.../authorized | 42 | What: /sys/bus/thunderbolt/devices/.../authorized |
| 17 | Date: Sep 2017 | 43 | Date: Sep 2017 |
| @@ -38,6 +64,13 @@ Description: This attribute is used to authorize Thunderbolt devices · 20 unmodified lines | |||
| 38 | the device did not contain a key at all, and | 64 | the device did not contain a key at all, and |
| 39 | EKEYREJECTED if the challenge response did not match. | 65 | EKEYREJECTED if the challenge response did not match. |
| 40 | 66 | ||
| 67 | What: /sys/bus/thunderbolt/devices/.../boot | ||
| 68 | Date: Jun 2018 | ||
| 69 | KernelVersion: 4.17 | ||
| 70 | Contact: thunderbolt-software@lists.01.org | ||
| 71 | Description: This attribute contains 1 if Thunderbolt device was already | ||
| 72 | authorized on boot and 0 otherwise. | ||
| 73 | |||
| 41 | What: /sys/bus/thunderbolt/devices/.../key | 74 | What: /sys/bus/thunderbolt/devices/.../key |
| 42 | Date: Sep 2017 | 75 | Date: Sep 2017 |
| 43 | KernelVersion: 4.13 | 76 | KernelVersion: 4.13 |
| @@ -45,3 +45,12 @@ Contact: Tomas Winkler <tomas.winkler@intel.com> · 44 unmodified lines | |||
| 45 | Description: Display the driver HBM protocol version. | 45 | Description: Display the driver HBM protocol version. |
| 46 | 46 | ||
| 47 | The HBM protocol version supported by the driver. | 47 | The HBM protocol version supported by the driver. |
| 48 | |||
| 49 | What: /sys/class/mei/meiN/tx_queue_limit | ||
| 50 | Date: Jan 2018 | ||
| 51 | KernelVersion: 4.16 | ||
| 52 | Contact: Tomas Winkler <tomas.winkler@intel.com> | ||
| 53 | Description: Configure tx queue limit | ||
| 54 | |||
| 55 | Set maximal number of pending writes | ||
| 56 | per opened session. | ||
| @@ -0,0 +1,10 @@ | |||
| 1 | What: /sys/bus/platform/devices/[..]/fsi-master-gpio/external_mode | ||
| 2 | Date: Feb 2018 | ||
| 3 | KernelVersion: 4.17 | ||
| 4 | Contact: jk@ozlabs.org | ||
| 5 | Description: | ||
| 6 | Controls access arbitration for GPIO-based FSI master. A | ||
| 7 | value of 0 (the default) sets normal mode, where the | ||
| 8 | driver performs FSI bus transactions, 1 sets external mode, | ||
| 9 | where the FSI bus is driven externally (for example, by | ||
| 10 | a debug device). | ||
| @@ -21,11 +21,11 @@ vulnerable to DMA attacks. · 20 unmodified lines | |||
| 21 | Security levels and how to use them | 21 | Security levels and how to use them |
| 22 | ----------------------------------- | 22 | ----------------------------------- |
| 23 | Starting with Intel Falcon Ridge Thunderbolt controller there are 4 | 23 | Starting with Intel Falcon Ridge Thunderbolt controller there are 4 |
| 24 | security levels available. The reason for these is the fact that the | 24 | security levels available. Intel Titan Ridge added one more security level |
| 25 | connected devices can be DMA masters and thus read contents of the host | 25 | (usbonly). The reason for these is the fact that the connected devices can |
| 26 | memory without CPU and OS knowing about it. There are ways to prevent | 26 | be DMA masters and thus read contents of the host memory without CPU and OS |
| 27 | this by setting up an IOMMU but it is not always available for various | 27 | knowing about it. There are ways to prevent this by setting up an IOMMU but |
| 28 | reasons. | 28 | it is not always available for various reasons. |
| 29 | 29 | ||
| 30 | The security levels are as follows: | 30 | The security levels are as follows: |
| 31 | 31 | ||
| @@ -52,6 +52,11 @@ The security levels are as follows: · 20 unmodified lines | |||
| 52 | USB. No PCIe tunneling is done. In BIOS settings this is | 52 | USB. No PCIe tunneling is done. In BIOS settings this is |
| 53 | typically called *Display Port Only*. | 53 | typically called *Display Port Only*. |
| 54 | 54 | ||
| 55 | usbonly | ||
| 56 | The firmware automatically creates tunnels for the USB controller and | ||
| 57 | Display Port in a dock. All PCIe links downstream of the dock are | ||
| 58 | removed. | ||
| 59 | |||
| 55 | The current security level can be read from | 60 | The current security level can be read from |
| 56 | ``/sys/bus/thunderbolt/devices/domainX/security`` where ``domainX`` is | 61 | ``/sys/bus/thunderbolt/devices/domainX/security`` where ``domainX`` is |
| 57 | the Thunderbolt domain the host controller manages. There is typically | 62 | the Thunderbolt domain the host controller manages. There is typically |
| @@ -0,0 +1,49 @@ | |||
| 1 | Samsung micro-USB 11-pin connector | ||
| 2 | ================================== | ||
| 3 | |||
| 4 | Samsung micro-USB 11-pin connector is an extension of micro-USB connector. | ||
| 5 | It is present in multiple Samsung mobile devices. | ||
| 6 | It has additional pins to route MHL traffic simultanously with USB. | ||
| 7 | |||
| 8 | The bindings are superset of usb-connector bindings for micro-USB connector[1]. | ||
| 9 | |||
| 10 | Required properties: | ||
| 11 | - compatible: must be: "samsung,usb-connector-11pin", "usb-b-connector", | ||
| 12 | - type: must be "micro". | ||
| 13 | |||
| 14 | Required nodes: | ||
| 15 | - any data bus to the connector should be modeled using the OF graph bindings | ||
| 16 | specified in bindings/graph.txt, unless the bus is between parent node and | ||
| 17 | the connector. Since single connector can have multpile data buses every bus | ||
| 18 | has assigned OF graph port number as follows: | ||
| 19 | 0: High Speed (HS), | ||
| 20 | 3: Mobile High-Definition Link (MHL), specific to 11-pin Samsung micro-USB. | ||
| 21 | |||
| 22 | [1]: bindings/connector/usb-connector.txt | ||
| 23 | |||
| 24 | Example | ||
| 25 | ------- | ||
| 26 | |||
| 27 | Micro-USB connector with HS lines routed via controller (MUIC) and MHL lines | ||
| 28 | connected to HDMI-MHL bridge (sii8620): | ||
| 29 | |||
| 30 | muic-max77843@66 { | ||
| 31 | ... | ||
| 32 | usb_con: connector { | ||
| 33 | compatible = "samsung,usb-connector-11pin", "usb-b-connector"; | ||
| 34 | label = "micro-USB"; | ||
| 35 | type = "micro"; | ||
| 36 | |||
| 37 | ports { | ||
| 38 | #address-cells = <1>; | ||
| 39 | #size-cells = <0>; | ||
| 40 | |||
| 41 | port@3 { | ||
| 42 | reg = <3>; | ||
| 43 | usb_con_mhl: endpoint { | ||
| 44 | remote-endpoint = <&sii8620_mhl>; | ||
| 45 | }; | ||
| 46 | }; | ||
| 47 | }; | ||
| 48 | }; | ||
| 49 | }; | ||
| @@ -0,0 +1,75 @@ | |||
| 1 | USB Connector | ||
| 2 | ============= | ||
| 3 | |||
| 4 | USB connector node represents physical USB connector. It should be | ||
| 5 | a child of USB interface controller. | ||
| 6 | |||
| 7 | Required properties: | ||
| 8 | - compatible: describes type of the connector, must be one of: | ||
| 9 | "usb-a-connector", | ||
| 10 | "usb-b-connector", | ||
| 11 | "usb-c-connector". | ||
| 12 | |||
| 13 | Optional properties: | ||
| 14 | - label: symbolic name for the connector, | ||
| 15 | - type: size of the connector, should be specified in case of USB-A, USB-B | ||
| 16 | non-fullsize connectors: "mini", "micro". | ||
| 17 | |||
| 18 | Required nodes: | ||
| 19 | - any data bus to the connector should be modeled using the OF graph bindings | ||
| 20 | specified in bindings/graph.txt, unless the bus is between parent node and | ||
| 21 | the connector. Since single connector can have multpile data buses every bus | ||
| 22 | has assigned OF graph port number as follows: | ||
| 23 | 0: High Speed (HS), present in all connectors, | ||
| 24 | 1: Super Speed (SS), present in SS capable connectors, | ||
| 25 | 2: Sideband use (SBU), present in USB-C. | ||
| 26 | |||
| 27 | Examples | ||
| 28 | -------- | ||
| 29 | |||
| 30 | 1. Micro-USB connector with HS lines routed via controller (MUIC): | ||
| 31 | |||
| 32 | muic-max77843@66 { | ||
| 33 | ... | ||
| 34 | usb_con: connector { | ||
| 35 | compatible = "usb-b-connector"; | ||
| 36 | label = "micro-USB"; | ||
| 37 | type = "micro"; | ||
| 38 | }; | ||
| 39 | }; | ||
| 40 | |||
| 41 | 2. USB-C connector attached to CC controller (s2mm005), HS lines routed | ||
| 42 | to companion PMIC (max77865), SS lines to USB3 PHY and SBU to DisplayPort. | ||
| 43 | DisplayPort video lines are routed to the connector via SS mux in USB3 PHY. | ||
| 44 | |||
| 45 | ccic: s2mm005@33 { | ||
| 46 | ... | ||
| 47 | usb_con: connector { | ||
| 48 | compatible = "usb-c-connector"; | ||
| 49 | label = "USB-C"; | ||
| 50 | |||
| 51 | ports { | ||
| 52 | #address-cells = <1>; | ||
| 53 | #size-cells = <0>; | ||
| 54 | |||
| 55 | port@0 { | ||
| 56 | reg = <0>; | ||
| 57 | usb_con_hs: endpoint { | ||
| 58 | remote-endpoint = <&max77865_usbc_hs>; | ||
| 59 | }; | ||
| 60 | }; | ||
| 61 | port@1 { | ||
| 62 | reg = <1>; | ||
| 63 | usb_con_ss: endpoint { | ||
| 64 | remote-endpoint = <&usbdrd_phy_ss>; | ||
| 65 | }; | ||
| 66 | }; | ||
| 67 | port@2 { | ||
| 68 | reg = <2>; | ||
| 69 | usb_con_sbu: endpoint { | ||
| 70 | remote-endpoint = <&dp_aux>; | ||
| 71 | }; | ||
| 72 | }; | ||
| 73 | }; | ||
| 74 | }; | ||
| 75 | }; | ||
| @@ -0,0 +1,151 @@ | |||
| 1 | FSI bus & engine generic device tree bindings | ||
| 2 | ============================================= | ||
| 3 | |||
| 4 | The FSI bus is probe-able, so the OS is able to enumerate FSI slaves, and | ||
| 5 | engines within those slaves. However, we have a facility to match devicetree | ||
| 6 | nodes to probed engines. This allows for fsi engines to expose non-probeable | ||
| 7 | busses, which are then exposed by the device tree. For example, an FSI engine | ||
| 8 | that is an I2C master - the I2C bus can be described by the device tree under | ||
| 9 | the engine's device tree node. | ||
| 10 | |||
| 11 | FSI masters may require their own DT nodes (to describe the master HW itself); | ||
| 12 | that requirement is defined by the master's implementation, and is described by | ||
| 13 | the fsi-master-* binding specifications. | ||
| 14 | |||
| 15 | Under the masters' nodes, we can describe the bus topology using nodes to | ||
| 16 | represent the FSI slaves and their slave engines. As a basic outline: | ||
| 17 | |||
| 18 | fsi-master { | ||
| 19 | /* top-level of FSI bus topology, bound to an FSI master driver and | ||
| 20 | * exposes an FSI bus */ | ||
| 21 | |||
| 22 | fsi-slave@<link,id> { | ||
| 23 | /* this node defines the FSI slave device, and is handled | ||
| 24 | * entirely with FSI core code */ | ||
| 25 | |||
| 26 | fsi-slave-engine@<addr> { | ||
| 27 | /* this node defines the engine endpoint & address range, which | ||
| 28 | * is bound to the relevant fsi device driver */ | ||
| 29 | ... | ||
| 30 | }; | ||
| 31 | |||
| 32 | fsi-slave-engine@<addr> { | ||
| 33 | ... | ||
| 34 | }; | ||
| 35 | |||
| 36 | }; | ||
| 37 | }; | ||
| 38 | |||
| 39 | Note that since the bus is probe-able, some (or all) of the topology may | ||
| 40 | not be described; this binding only provides an optional facility for | ||
| 41 | adding subordinate device tree nodes as children of FSI engines. | ||
| 42 | |||
| 43 | FSI masters | ||
| 44 | ----------- | ||
| 45 | |||
| 46 | FSI master nodes declare themselves as such with the "fsi-master" compatible | ||
| 47 | value. It's likely that an implementation-specific compatible value will | ||
| 48 | be needed as well, for example: | ||
| 49 | |||
| 50 | compatible = "fsi-master-gpio", "fsi-master"; | ||
| 51 | |||
| 52 | Since the master nodes describe the top-level of the FSI topology, they also | ||
| 53 | need to declare the FSI-standard addressing scheme. This requires two cells for | ||
| 54 | addresses (link index and slave ID), and no size: | ||
| 55 | |||
| 56 | #address-cells = <2>; | ||
| 57 | #size-cells = <0>; | ||
| 58 | |||
| 59 | An optional boolean property can be added to indicate that a particular master | ||
| 60 | should not scan for connected devices at initialization time. This is | ||
| 61 | necessary in cases where a scan could cause arbitration issues with other | ||
| 62 | masters that may be present on the bus. | ||
| 63 | |||
| 64 | no-scan-on-init; | ||
| 65 | |||
| 66 | FSI slaves | ||
| 67 | ---------- | ||
| 68 | |||
| 69 | Slaves are identified by a (link-index, slave-id) pair, so require two cells | ||
| 70 | for an address identifier. Since these are not a range, no size cells are | ||
| 71 | required. For an example, a slave on link 1, with ID 2, could be represented | ||
| 72 | as: | ||
| 73 | |||
| 74 | cfam@1,2 { | ||
| 75 | reg = <1 2>; | ||
| 76 | [...]; | ||
| 77 | } | ||
| 78 | |||
| 79 | Each slave provides an address-space, under which the engines are accessible. | ||
| 80 | That address space has a maximum of 23 bits, so we use one cell to represent | ||
| 81 | addresses and sizes in the slave address space: | ||
| 82 | |||
| 83 | #address-cells = <1>; | ||
| 84 | #size-cells = <1>; | ||
| 85 | |||
| 86 | |||
| 87 | FSI engines (devices) | ||
| 88 | --------------------- | ||
| 89 | |||
| 90 | Engines are identified by their address under the slaves' address spaces. We | ||
| 91 | use a single cell for address and size. Engine nodes represent the endpoint | ||
| 92 | FSI device, and are passed to those FSI device drivers' ->probe() functions. | ||
| 93 | |||
| 94 | For example, for a slave using a single 0x400-byte page starting at address | ||
| 95 | 0xc00: | ||
| 96 | |||
| 97 | engine@c00 { | ||
| 98 | reg = <0xc00 0x400>; | ||
| 99 | }; | ||
| 100 | |||
| 101 | |||
| 102 | Full example | ||
| 103 | ------------ | ||
| 104 | |||
| 105 | Here's an example that illustrates: | ||
| 106 | - an FSI master | ||
| 107 | - connected to an FSI slave | ||
| 108 | - that contains an engine that is an I2C master | ||
| 109 | - connected to an I2C EEPROM | ||
| 110 | |||
| 111 | The FSI master may be connected to additional slaves, and slaves may have | ||
| 112 | additional engines, but they don't necessarily need to be describe in the | ||
| 113 | device tree if no extra platform information is required. | ||
| 114 | |||
| 115 | /* The GPIO-based FSI master node, describing the top level of the | ||
| 116 | * FSI bus | ||
| 117 | */ | ||
| 118 | gpio-fsi { | ||
| 119 | compatible = "fsi-master-gpio", "fsi-master"; | ||
| 120 | #address-cells = <2>; | ||
| 121 | #size-cells = <0>; | ||
| 122 | |||
| 123 | /* A FSI slave (aka. CFAM) at link 0, ID 0. */ | ||
| 124 | cfam@0,0 { | ||
| 125 | reg = <0 0>; | ||
| 126 | #address-cells = <1>; | ||
| 127 | #size-cells = <1>; | ||
| 128 | |||
| 129 | /* FSI engine at 0xc00, using a single page. In this example, | ||
| 130 | * it's an I2C master controller, so subnodes describe the | ||
| 131 | * I2C bus. | ||
| 132 | */ | ||
| 133 | i2c-controller@c00 { | ||
| 134 | reg = <0xc00 0x400>; | ||
| 135 | |||
| 136 | /* Engine-specific data. In this case, we're describing an | ||
| 137 | * I2C bus, so we're conforming to the generic I2C binding | ||
| 138 | */ | ||
| 139 | compatible = "some-vendor,fsi-i2c-controller"; | ||
| 140 | #address-cells = <1>; | ||
| 141 | #size-cells = <1>; | ||
| 142 | |||
| 143 | /* I2C endpoint device: an Atmel EEPROM */ | ||
| 144 | eeprom@50 { | ||
| 145 | compatible = "atmel,24c256"; | ||
| 146 | reg = <0x50>; | ||
| 147 | pagesize = <64>; | ||
| 148 | }; | ||
| 149 | }; | ||
| 150 | }; | ||
| 151 | }; | ||
| @@ -109,9 +109,50 @@ lpc: lpc@1e789000 { · 108 unmodified lines | |||
| 109 | }; | 109 | }; |
| 110 | }; | 110 | }; |
| 111 | 111 | ||
| 112 | BMC Node Children | ||
| 113 | ================== | ||
| 114 | |||
| 115 | |||
| 112 | Host Node Children | 116 | Host Node Children |
| 113 | ================== | 117 | ================== |
| 114 | 118 | ||
| 119 | LPC Host Interface Controller | ||
| 120 | ------------------- | ||
| 121 | |||
| 122 | The LPC Host Interface Controller manages functions exposed to the host such as | ||
| 123 | LPC firmware hub cycles, configuration of the LPC-to-AHB mapping, UART | ||
| 124 | management and bus snoop configuration. | ||
| 125 | |||
| 126 | Required properties: | ||
| 127 | |||
| 128 | - compatible: One of: | ||
| 129 | "aspeed,ast2400-lpc-ctrl"; | ||
| 130 | "aspeed,ast2500-lpc-ctrl"; | ||
| 131 | |||
| 132 | - reg: contains offset/length values of the host interface controller | ||
| 133 | memory regions | ||
| 134 | |||
| 135 | - clocks: contains a phandle to the syscon node describing the clocks. | ||
| 136 | There should then be one cell representing the clock to use | ||
| 137 | |||
| 138 | - memory-region: A phandle to a reserved_memory region to be used for the LPC | ||
| 139 | to AHB mapping | ||
| 140 | |||
| 141 | - flash: A phandle to the SPI flash controller containing the flash to | ||
| 142 | be exposed over the LPC to AHB mapping | ||
| 143 | |||
| 144 | Example: | ||
| 145 | |||
| 146 | lpc-host@80 { | ||
| 147 | lpc_ctrl: lpc-ctrl@0 { | ||
| 148 | compatible = "aspeed,ast2500-lpc-ctrl"; | ||
| 149 | reg = <0x0 0x80>; | ||
| 150 | clocks = <&syscon ASPEED_CLK_GATE_LCLK>; | ||
| 151 | memory-region = <&flash_memory>; | ||
| 152 | flash = <&spi>; | ||
| 153 | }; | ||
| 154 | }; | ||
| 155 | |||
| 115 | LPC Host Controller | 156 | LPC Host Controller |
| 116 | ------------------- | 157 | ------------------- |
| 117 | 158 | ||
| @@ -11,17 +11,32 @@ Required properties: · 10 unmodified lines | |||
| 11 | "fsl,imx6ul-ocotp" (i.MX6UL), | 11 | "fsl,imx6ul-ocotp" (i.MX6UL), |
| 12 | "fsl,imx7d-ocotp" (i.MX7D/S), | 12 | "fsl,imx7d-ocotp" (i.MX7D/S), |
| 13 | followed by "syscon". | 13 | followed by "syscon". |
| 14 | - #address-cells : Should be 1 | ||
| 15 | - #size-cells : Should be 1 | ||
| 14 | - reg: Should contain the register base and length. | 16 | - reg: Should contain the register base and length. |
| 15 | - clocks: Should contain a phandle pointing to the gated peripheral clock. | 17 | - clocks: Should contain a phandle pointing to the gated peripheral clock. |
| 16 | 18 | ||
| 17 | Optional properties: | 19 | Optional properties: |
| 18 | - read-only: disable write access | 20 | - read-only: disable write access |
| 19 | 21 | ||
| 20 | Example: | 22 | Optional Child nodes: |
| 23 | |||
| 24 | - Data cells of ocotp: | ||
| 25 | Detailed bindings are described in bindings/nvmem/nvmem.txt | ||
| 21 | 26 | ||
| 27 | Example: | ||
| 22 | ocotp: ocotp@21bc000 { | 28 | ocotp: ocotp@21bc000 { |
| 23 | compatible = "fsl,imx6q-ocotp", "syscon"; | 29 | #address-cells = <1>; |
| 30 | #size-cells = <1>; | ||
| 31 | compatible = "fsl,imx6sx-ocotp", "syscon"; | ||
| 24 | reg = <0x021bc000 0x4000>; | 32 | reg = <0x021bc000 0x4000>; |
| 25 | clocks = <&clks IMX6QDL_CLK_IIM>; | 33 | clocks = <&clks IMX6SX_CLK_OCOTP>; |
| 26 | read-only; | 34 | |
| 35 | tempmon_calib: calib@38 { | ||
| 36 | reg = <0x38 4>; | ||
| 37 | }; | ||
| 38 | |||
| 39 | tempmon_temp_grade: temp-grade@20 { | ||
| 40 | reg = <0x20 4>; | ||
| 41 | }; | ||
| 27 | }; | 42 | }; |
| @@ -1,5 +1,5 @@ | |||
| 1 | Device tree bindings for Low Power General Purpose Register found in i.MX6Q/D | 1 | Device tree bindings for Low Power General Purpose Register found in i.MX6Q/D |
| 2 | Secure Non-Volatile Storage. | 2 | and i.MX7 Secure Non-Volatile Storage. |
| 3 | 3 | ||
| 4 | This DT node should be represented as a sub-node of a "syscon", | 4 | This DT node should be represented as a sub-node of a "syscon", |
| 5 | "simple-mfd" node. | 5 | "simple-mfd" node. |
| @@ -8,6 +8,7 @@ Required properties: · 2 unmodified lines | |||
| 8 | - compatible: should be one of the fallowing variants: | 8 | - compatible: should be one of the fallowing variants: |
| 9 | "fsl,imx6q-snvs-lpgpr" for Freescale i.MX6Q/D/DL/S | 9 | "fsl,imx6q-snvs-lpgpr" for Freescale i.MX6Q/D/DL/S |
| 10 | "fsl,imx6ul-snvs-lpgpr" for Freescale i.MX6UL | 10 | "fsl,imx6ul-snvs-lpgpr" for Freescale i.MX6UL |
| 11 | "fsl,imx7d-snvs-lpgpr" for Freescale i.MX7D/S | ||
| 11 | 12 | ||
| 12 | Example: | 13 | Example: |
| 13 | snvs: snvs@020cc000 { | 14 | snvs: snvs@020cc000 { |
| @@ -709,6 +709,11 @@ The vmbus device regions are mapped into uio device resources: · 708 unmodified lines | |||
| 709 | 3) Network receive buffer region | 709 | 3) Network receive buffer region |
| 710 | 4) Network send buffer region | 710 | 4) Network send buffer region |
| 711 | 711 | ||
| 712 | If a subchannel is created by a request to host, then the uio_hv_generic | ||
| 713 | device driver will create a sysfs binary file for the per-channel ring buffer. | ||
| 714 | For example: | ||
| 715 | /sys/bus/vmbus/devices/3811fe4d-0fa0-4b62-981a-74fc1084c757/channels/21/ring | ||
| 716 | |||
| 712 | Further information | 717 | Further information |
| 713 | =================== | 718 | =================== |
| 714 | 719 | ||
| @@ -6212,6 +6212,11 @@ F: Documentation/hw_random.txt · 6211 unmodified lines | |||
| 6212 | F: drivers/char/hw_random/ | 6212 | F: drivers/char/hw_random/ |
| 6213 | F: include/linux/hw_random.h | 6213 | F: include/linux/hw_random.h |
| 6214 | 6214 | ||
| 6215 | HARDWARE TRACING FACILITIES | ||
| 6216 | M: Alexander Shishkin <alexander.shishkin@linux.intel.com> | ||
| 6217 | S: Maintained | ||
| 6218 | F: drivers/hwtracing/ | ||
| 6219 | |||
| 6215 | HARDWARE SPINLOCK CORE | 6220 | HARDWARE SPINLOCK CORE |
| 6216 | M: Ohad Ben-Cohen <ohad@wizery.com> | 6221 | M: Ohad Ben-Cohen <ohad@wizery.com> |
| 6217 | M: Bjorn Andersson <bjorn.andersson@linaro.org> | 6222 | M: Bjorn Andersson <bjorn.andersson@linaro.org> |
| @@ -8140,7 +8145,7 @@ F: drivers/*/*/*pasemi* · 1922 unmodified lines | |||
| 8140 | LINUX KERNEL DUMP TEST MODULE (LKDTM) | 8145 | LINUX KERNEL DUMP TEST MODULE (LKDTM) |
| 8141 | M: Kees Cook <keescook@chromium.org> | 8146 | M: Kees Cook <keescook@chromium.org> |
| 8142 | S: Maintained | 8147 | S: Maintained |
| 8143 | F: drivers/misc/lkdtm* | 8148 | F: drivers/misc/lkdtm/* |
| 8144 | 8149 | ||
| 8145 | LINUX KERNEL MEMORY CONSISTENCY MODEL (LKMM) | 8150 | LINUX KERNEL MEMORY CONSISTENCY MODEL (LKMM) |
| 8146 | M: Alan Stern <stern@rowland.harvard.edu> | 8151 | M: Alan Stern <stern@rowland.harvard.edu> |
| @@ -902,6 +902,9 @@ BUILD_INTERRUPT3(hyperv_callback_vector, HYPERVISOR_CALLBACK_VECTOR, · 901 unmodified lines | |||
| 902 | BUILD_INTERRUPT3(hyperv_reenlightenment_vector, HYPERV_REENLIGHTENMENT_VECTOR, | 902 | BUILD_INTERRUPT3(hyperv_reenlightenment_vector, HYPERV_REENLIGHTENMENT_VECTOR, |
| 903 | hyperv_reenlightenment_intr) | 903 | hyperv_reenlightenment_intr) |
| 904 | 904 | ||
| 905 | BUILD_INTERRUPT3(hv_stimer0_callback_vector, HYPERV_STIMER0_VECTOR, | ||
| 906 | hv_stimer0_vector_handler) | ||
| 907 | |||
| 905 | #endif /* CONFIG_HYPERV */ | 908 | #endif /* CONFIG_HYPERV */ |
| 906 | 909 | ||
| 907 | ENTRY(page_fault) | 910 | ENTRY(page_fault) |
| @@ -1140,6 +1140,9 @@ apicinterrupt3 HYPERVISOR_CALLBACK_VECTOR \ · 1139 unmodified lines | |||
| 1140 | 1140 | ||
| 1141 | apicinterrupt3 HYPERV_REENLIGHTENMENT_VECTOR \ | 1141 | apicinterrupt3 HYPERV_REENLIGHTENMENT_VECTOR \ |
| 1142 | hyperv_reenlightenment_vector hyperv_reenlightenment_intr | 1142 | hyperv_reenlightenment_vector hyperv_reenlightenment_intr |
| 1143 | |||
| 1144 | apicinterrupt3 HYPERV_STIMER0_VECTOR \ | ||
| 1145 | hv_stimer0_callback_vector hv_stimer0_vector_handler | ||
| 1143 | #endif /* CONFIG_HYPERV */ | 1146 | #endif /* CONFIG_HYPERV */ |
| 1144 | 1147 | ||
| 1145 | idtentry debug do_debug has_error_code=0 paranoid=1 shift_ist=DEBUG_STACK | 1148 | idtentry debug do_debug has_error_code=0 paranoid=1 shift_ist=DEBUG_STACK |
| @@ -40,6 +40,7 @@ typedef struct { · 39 unmodified lines | |||
| 40 | #endif | 40 | #endif |
| 41 | #if IS_ENABLED(CONFIG_HYPERV) | 41 | #if IS_ENABLED(CONFIG_HYPERV) |
| 42 | unsigned int irq_hv_reenlightenment_count; | 42 | unsigned int irq_hv_reenlightenment_count; |
| 43 | unsigned int hyperv_stimer0_count; | ||
| 43 | #endif | 44 | #endif |
| 44 | } ____cacheline_aligned irq_cpustat_t; | 45 | } ____cacheline_aligned irq_cpustat_t; |
| 45 | 46 | ||
| @@ -106,9 +106,10 @@ · 105 unmodified lines | |||
| 106 | 106 | ||
| 107 | #if IS_ENABLED(CONFIG_HYPERV) | 107 | #if IS_ENABLED(CONFIG_HYPERV) |
| 108 | #define HYPERV_REENLIGHTENMENT_VECTOR 0xee | 108 | #define HYPERV_REENLIGHTENMENT_VECTOR 0xee |
| 109 | #define HYPERV_STIMER0_VECTOR 0xed | ||
| 109 | #endif | 110 | #endif |
| 110 | 111 | ||
| 111 | #define LOCAL_TIMER_VECTOR 0xed | 112 | #define LOCAL_TIMER_VECTOR 0xec |
| 112 | 113 | ||
| 113 | #define NR_VECTORS 256 | 114 | #define NR_VECTORS 256 |
| 114 | 115 | ||
| @@ -173,6 +173,19 @@ void hv_remove_kexec_handler(void); · 172 unmodified lines | |||
| 173 | void hv_setup_crash_handler(void (*handler)(struct pt_regs *regs)); | 173 | void hv_setup_crash_handler(void (*handler)(struct pt_regs *regs)); |
| 174 | void hv_remove_crash_handler(void); | 174 | void hv_remove_crash_handler(void); |
| 175 | 175 | ||
| 176 | /* | ||
| 177 | * Routines for stimer0 Direct Mode handling. | ||
| 178 | * On x86/x64, there are no percpu actions to take. | ||
| 179 | */ | ||
| 180 | void hv_stimer0_vector_handler(struct pt_regs *regs); | ||
| 181 | void hv_stimer0_callback_vector(void); | ||
| 182 | int hv_setup_stimer0_irq(int *irq, int *vector, void (*handler)(void)); | ||
| 183 | void hv_remove_stimer0_irq(int irq); | ||
| 184 | |||
| 185 | static inline void hv_enable_stimer0_percpu_irq(int irq) {} | ||
| 186 | static inline void hv_disable_stimer0_percpu_irq(int irq) {} | ||
| 187 | |||
| 188 | |||
| 176 | #if IS_ENABLED(CONFIG_HYPERV) | 189 | #if IS_ENABLED(CONFIG_HYPERV) |
| 177 | extern struct clocksource *hyperv_cs; | 190 | extern struct clocksource *hyperv_cs; |
| 178 | extern void *hv_hypercall_pg; | 191 | extern void *hv_hypercall_pg; |
| @@ -77,6 +77,9 @@ · 76 unmodified lines | |||
| 77 | /* Crash MSR available */ | 77 | /* Crash MSR available */ |
| 78 | #define HV_FEATURE_GUEST_CRASH_MSR_AVAILABLE (1 << 10) | 78 | #define HV_FEATURE_GUEST_CRASH_MSR_AVAILABLE (1 << 10) |
| 79 | 79 | ||
| 80 | /* stimer Direct Mode is available */ | ||
| 81 | #define HV_X64_STIMER_DIRECT_MODE_AVAILABLE (1 << 19) | ||
| 82 | |||
| 80 | /* | 83 | /* |
| 81 | * Feature identification: EBX indicates which flags were specified at | 84 | * Feature identification: EBX indicates which flags were specified at |
| 82 | * partition creation. The format is the same as the partition creation | 85 | * partition creation. The format is the same as the partition creation |
| @@ -37,6 +37,7 @@ EXPORT_SYMBOL_GPL(ms_hyperv); · 36 unmodified lines | |||
| 37 | 37 | ||
| 38 | #if IS_ENABLED(CONFIG_HYPERV) | 38 | #if IS_ENABLED(CONFIG_HYPERV) |
| 39 | static void (*vmbus_handler)(void); | 39 | static void (*vmbus_handler)(void); |
| 40 | static void (*hv_stimer0_handler)(void); | ||
| 40 | static void (*hv_kexec_handler)(void); | 41 | static void (*hv_kexec_handler)(void); |
| 41 | static void (*hv_crash_handler)(struct pt_regs *regs); | 42 | static void (*hv_crash_handler)(struct pt_regs *regs); |
| 42 | 43 | ||
| @@ -69,6 +70,41 @@ void hv_remove_vmbus_irq(void) · 26 unmodified lines | |||
| 69 | EXPORT_SYMBOL_GPL(hv_setup_vmbus_irq); | 70 | EXPORT_SYMBOL_GPL(hv_setup_vmbus_irq); |
| 70 | EXPORT_SYMBOL_GPL(hv_remove_vmbus_irq); | 71 | EXPORT_SYMBOL_GPL(hv_remove_vmbus_irq); |
| 71 | 72 | ||
| 73 | /* | ||
| 74 | * Routines to do per-architecture handling of stimer0 | ||
| 75 | * interrupts when in Direct Mode | ||
| 76 | */ | ||
| 77 | |||
| 78 | __visible void __irq_entry hv_stimer0_vector_handler(struct pt_regs *regs) | ||
| 79 | { | ||
| 80 | struct pt_regs *old_regs = set_irq_regs(regs); | ||
| 81 | |||
| 82 | entering_irq(); | ||
| 83 | inc_irq_stat(hyperv_stimer0_count); | ||
| 84 | if (hv_stimer0_handler) | ||
| 85 | hv_stimer0_handler(); | ||
| 86 | ack_APIC_irq(); | ||
| 87 | |||
| 88 | exiting_irq(); | ||
| 89 | set_irq_regs(old_regs); | ||
| 90 | } | ||
| 91 | |||
| 92 | int hv_setup_stimer0_irq(int *irq, int *vector, void (*handler)(void)) | ||
| 93 | { | ||
| 94 | *vector = HYPERV_STIMER0_VECTOR; | ||
| 95 | *irq = 0; /* Unused on x86/x64 */ | ||
| 96 | hv_stimer0_handler = handler; | ||
| 97 | return 0; | ||
| 98 | } | ||
| 99 | EXPORT_SYMBOL_GPL(hv_setup_stimer0_irq); | ||
| 100 | |||
| 101 | void hv_remove_stimer0_irq(int irq) | ||
| 102 | { | ||
| 103 | /* We have no way to deallocate the interrupt gate */ | ||
| 104 | hv_stimer0_handler = NULL; | ||
| 105 | } | ||
| 106 | EXPORT_SYMBOL_GPL(hv_remove_stimer0_irq); | ||
| 107 | |||
| 72 | void hv_setup_kexec_handler(void (*handler)(void)) | 108 | void hv_setup_kexec_handler(void (*handler)(void)) |
| 73 | { | 109 | { |
| 74 | hv_kexec_handler = handler; | 110 | hv_kexec_handler = handler; |
| @@ -257,6 +293,10 @@ static void __init ms_hyperv_init_platform(void) · 182 unmodified lines | |||
| 257 | alloc_intr_gate(HYPERV_REENLIGHTENMENT_VECTOR, | 293 | alloc_intr_gate(HYPERV_REENLIGHTENMENT_VECTOR, |
| 258 | hyperv_reenlightenment_vector); | 294 | hyperv_reenlightenment_vector); |
| 259 | 295 | ||
| 296 | /* Setup the IDT for stimer0 */ | ||
| 297 | if (ms_hyperv.misc_features & HV_X64_STIMER_DIRECT_MODE_AVAILABLE) | ||
| 298 | alloc_intr_gate(HYPERV_STIMER0_VECTOR, | ||
| 299 | hv_stimer0_callback_vector); | ||
| 260 | #endif | 300 | #endif |
| 261 | } | 301 | } |
| 262 | 302 | ||
| @@ -150,6 +150,13 @@ int arch_show_interrupts(struct seq_file *p, int prec) · 149 unmodified lines | |||
| 150 | irq_stats(j)->irq_hv_reenlightenment_count); | 150 | irq_stats(j)->irq_hv_reenlightenment_count); |
| 151 | seq_puts(p, " Hyper-V reenlightenment interrupts\n"); | 151 | seq_puts(p, " Hyper-V reenlightenment interrupts\n"); |
| 152 | } | 152 | } |
| 153 | if (test_bit(HYPERV_STIMER0_VECTOR, system_vectors)) { | ||
| 154 | seq_printf(p, "%*s: ", prec, "HVS"); | ||
| 155 | for_each_online_cpu(j) | ||
| 156 | seq_printf(p, "%10u ", | ||
| 157 | irq_stats(j)->hyperv_stimer0_count); | ||
| 158 | seq_puts(p, " Hyper-V stimer0 interrupts\n"); | ||
| 159 | } | ||
| 153 | #endif | 160 | #endif |
| 154 | seq_printf(p, "%*s: %10u\n", prec, "ERR", atomic_read(&irq_err_count)); | 161 | seq_printf(p, "%*s: %10u\n", prec, "ERR", atomic_read(&irq_err_count)); |
| 155 | #if defined(CONFIG_X86_IO_APIC) | 162 | #if defined(CONFIG_X86_IO_APIC) |
| @@ -308,19 +308,22 @@ void blkdev_show(struct seq_file *seqf, off_t offset) · 307 unmodified lines | |||
| 308 | /** | 308 | /** |
| 309 | * register_blkdev - register a new block device | 309 | * register_blkdev - register a new block device |
| 310 | * | 310 | * |
| 311 | * @major: the requested major device number [1..255]. If @major = 0, try to | 311 | * @major: the requested major device number [1..BLKDEV_MAJOR_MAX-1]. If |
| 312 | * allocate any unused major number. | 312 | * @major = 0, try to allocate any unused major number. |
| 313 | * @name: the name of the new block device as a zero terminated string | 313 | * @name: the name of the new block device as a zero terminated string |
| 314 | * | 314 | * |
| 315 | * The @name must be unique within the system. | 315 | * The @name must be unique within the system. |
| 316 | * | 316 | * |
| 317 | * The return value depends on the @major input parameter: | 317 | * The return value depends on the @major input parameter: |
| 318 | * | 318 | * |
| 319 | * - if a major device number was requested in range [1..255] then the | 319 | * - if a major device number was requested in range [1..BLKDEV_MAJOR_MAX-1] |
| 320 | * function returns zero on success, or a negative error code | 320 | * then the function returns zero on success, or a negative error code |
| 321 | * - if any unused major number was requested with @major = 0 parameter | 321 | * - if any unused major number was requested with @major = 0 parameter |
| 322 | * then the return value is the allocated major number in range | 322 | * then the return value is the allocated major number in range |
| 323 | * [1..255] or a negative error code otherwise | 323 | * [1..BLKDEV_MAJOR_MAX-1] or a negative error code otherwise |
| 324 | * | ||
| 325 | * See Documentation/admin-guide/devices.txt for the list of allocated | ||
| 326 | * major numbers. | ||
| 324 | */ | 327 | */ |
| 325 | int register_blkdev(unsigned int major, const char *name) | 328 | int register_blkdev(unsigned int major, const char *name) |
| 326 | { | 329 | { |
| @@ -347,8 +350,8 @@ int register_blkdev(unsigned int major, const char *name) · 20 unmodified lines | |||
| 347 | } | 350 | } |
| 348 | 351 | ||
| 349 | if (major >= BLKDEV_MAJOR_MAX) { | 352 | if (major >= BLKDEV_MAJOR_MAX) { |
| 350 | pr_err("register_blkdev: major requested (%d) is greater than the maximum (%d) for %s\n", | 353 | pr_err("register_blkdev: major requested (%u) is greater than the maximum (%u) for %s\n", |
| 351 | major, BLKDEV_MAJOR_MAX, name); | 354 | major, BLKDEV_MAJOR_MAX-1, name); |
| 352 | 355 | ||
| 353 | ret = -EINVAL; | 356 | ret = -EINVAL; |
| 354 | goto out; | 357 | goto out; |
| @@ -375,7 +378,7 @@ int register_blkdev(unsigned int major, const char *name) · 20 unmodified lines | |||
| 375 | ret = -EBUSY; | 378 | ret = -EBUSY; |
| 376 | 379 | ||
| 377 | if (ret < 0) { | 380 | if (ret < 0) { |
| 378 | printk("register_blkdev: cannot get major %d for %s\n", | 381 | printk("register_blkdev: cannot get major %u for %s\n", |
| 379 | major, name); | 382 | major, name); |
| 380 | kfree(p); | 383 | kfree(p); |
| 381 | } | 384 | } |
| @@ -199,9 +199,7 @@ source "drivers/dax/Kconfig" · 198 unmodified lines | |||
| 199 | 199 | ||
| 200 | source "drivers/nvmem/Kconfig" | 200 | source "drivers/nvmem/Kconfig" |
| 201 | 201 | ||
| 202 | source "drivers/hwtracing/stm/Kconfig" | 202 | source "drivers/hwtracing/Kconfig" |
| 203 | |||
| 204 | source "drivers/hwtracing/intel_th/Kconfig" | ||
| 205 | 203 | ||
| 206 | source "drivers/fpga/Kconfig" | 204 | source "drivers/fpga/Kconfig" |
| 207 | 205 | ||
| @@ -236,7 +236,7 @@ source "drivers/char/hw_random/Kconfig" · 235 unmodified lines | |||
| 236 | 236 | ||
| 237 | config NVRAM | 237 | config NVRAM |
| 238 | tristate "/dev/nvram support" | 238 | tristate "/dev/nvram support" |
| 239 | depends on ATARI || X86 || (ARM && RTC_DRV_CMOS) || GENERIC_NVRAM | 239 | depends on ATARI || X86 || GENERIC_NVRAM |
| 240 | ---help--- | 240 | ---help--- |
| 241 | If you say Y here and create a character special file /dev/nvram | 241 | If you say Y here and create a character special file /dev/nvram |
| 242 | with major number 10 and minor number 144 using mknod ("man mknod"), | 242 | with major number 10 and minor number 144 using mknod ("man mknod"), |
| @@ -137,7 +137,7 @@ static ssize_t read_mem(struct file *file, char __user *buf, · 136 unmodified lines | |||
| 137 | 137 | ||
| 138 | while (count > 0) { | 138 | while (count > 0) { |
| 139 | unsigned long remaining; | 139 | unsigned long remaining; |
| 140 | int allowed; | 140 | int allowed, probe; |
| 141 | 141 | ||
| 142 | sz = size_inside_page(p, count); | 142 | sz = size_inside_page(p, count); |
| 143 | 143 | ||
| @@ -160,9 +160,9 @@ static ssize_t read_mem(struct file *file, char __user *buf, · 16 unmodified lines | |||
| 160 | if (!ptr) | 160 | if (!ptr) |
| 161 | goto failed; | 161 | goto failed; |
| 162 | 162 | ||
| 163 | err = probe_kernel_read(bounce, ptr, sz); | 163 | probe = probe_kernel_read(bounce, ptr, sz); |
| 164 | unxlate_dev_mem_ptr(p, ptr); | 164 | unxlate_dev_mem_ptr(p, ptr); |
| 165 | if (err) | 165 | if (probe) |
| 166 | goto failed; | 166 | goto failed; |
| 167 | 167 | ||
| 168 | remaining = copy_to_user(buf, bounce, sz); | 168 | remaining = copy_to_user(buf, bounce, sz); |
| @@ -24,7 +24,6 @@ MODULE_LICENSE("GPL v2"); · 23 unmodified lines | |||
| 24 | 24 | ||
| 25 | #define PCI_DEVICE_ID_XILLYBUS 0xebeb | 25 | #define PCI_DEVICE_ID_XILLYBUS 0xebeb |
| 26 | 26 | ||
| 27 | #define PCI_VENDOR_ID_ALTERA 0x1172 | ||
| 28 | #define PCI_VENDOR_ID_ACTEL 0x11aa | 27 | #define PCI_VENDOR_ID_ACTEL 0x11aa |
| 29 | #define PCI_VENDOR_ID_LATTICE 0x1204 | 28 | #define PCI_VENDOR_ID_LATTICE 0x1204 |
| 30 | 29 | ||
| @@ -18,8 +18,6 @@ · 17 unmodified lines | |||
| 18 | */ | 18 | */ |
| 19 | 19 | ||
| 20 | #include <linux/extcon-provider.h> | 20 | #include <linux/extcon-provider.h> |
| 21 | #include <linux/extcon/extcon-gpio.h> | ||
| 22 | #include <linux/gpio.h> | ||
| 23 | #include <linux/gpio/consumer.h> | 21 | #include <linux/gpio/consumer.h> |
| 24 | #include <linux/init.h> | 22 | #include <linux/init.h> |
| 25 | #include <linux/interrupt.h> | 23 | #include <linux/interrupt.h> |
| @@ -29,14 +27,30 @@ · 3 unmodified lines | |||
| 29 | #include <linux/slab.h> | 27 | #include <linux/slab.h> |
| 30 | #include <linux/workqueue.h> | 28 | #include <linux/workqueue.h> |
| 31 | 29 | ||
| 30 | /** | ||
| 31 | * struct gpio_extcon_data - A simple GPIO-controlled extcon device state container. | ||
| 32 | * @edev: Extcon device. | ||
| 33 | * @irq: Interrupt line for the external connector. | ||
| 34 | * @work: Work fired by the interrupt. | ||
| 35 | * @debounce_jiffies: Number of jiffies to wait for the GPIO to stabilize, from the debounce | ||
| 36 | * value. | ||
| 37 | * @gpiod: GPIO descriptor for this external connector. | ||
| 38 | * @extcon_id: The unique id of specific external connector. | ||
| 39 | * @debounce: Debounce time for GPIO IRQ in ms. | ||
| 40 | * @irq_flags: IRQ Flags (e.g., IRQF_TRIGGER_LOW). | ||
| 41 | * @check_on_resume: Boolean describing whether to check the state of gpio | ||
| 42 | * while resuming from sleep. | ||
| 43 | */ | ||
| 32 | struct gpio_extcon_data { | 44 | struct gpio_extcon_data { |
| 33 | struct extcon_dev *edev; | 45 | struct extcon_dev *edev; |
| 34 | int irq; | 46 | int irq; |
| 35 | struct delayed_work work; | 47 | struct delayed_work work; |
| 36 | unsigned long debounce_jiffies; | 48 | unsigned long debounce_jiffies; |
| 37 | 49 | struct gpio_desc *gpiod; | |
| 38 | struct gpio_desc *id_gpiod; | 50 | unsigned int extcon_id; |
| 39 | struct gpio_extcon_pdata *pdata; | 51 | unsigned long debounce; |
| 52 | unsigned long irq_flags; | ||
| 53 | bool check_on_resume; | ||
| 40 | }; | 54 | }; |
| 41 | 55 | ||
| 42 | static void gpio_extcon_work(struct work_struct *work) | 56 | static void gpio_extcon_work(struct work_struct *work) |
| @@ -46,11 +60,8 @@ static void gpio_extcon_work(struct work_struct *work) · 3 unmodified lines | |||
| 46 | container_of(to_delayed_work(work), struct gpio_extcon_data, | 60 | container_of(to_delayed_work(work), struct gpio_extcon_data, |
| 47 | work); | 61 | work); |
| 48 | 62 | ||
| 49 | state = gpiod_get_value_cansleep(data->id_gpiod); | 63 | state = gpiod_get_value_cansleep(data->gpiod); |
| 50 | if (data->pdata->gpio_active_low) | 64 | extcon_set_state_sync(data->edev, data->extcon_id, state); |
| 51 | state = !state; | ||
| 52 | |||
| 53 | extcon_set_state_sync(data->edev, data->pdata->extcon_id, state); | ||
| 54 | } | 65 | } |
| 55 | 66 | ||
| 56 | static irqreturn_t gpio_irq_handler(int irq, void *dev_id) | 67 | static irqreturn_t gpio_irq_handler(int irq, void *dev_id) |
| @@ -62,65 +73,41 @@ static irqreturn_t gpio_irq_handler(int irq, void *dev_id) · 5 unmodified lines | |||
| 62 | return IRQ_HANDLED; | 73 | return IRQ_HANDLED; |
| 63 | } | 74 | } |
| 64 | 75 | ||
| 65 | static int gpio_extcon_init(struct device *dev, struct gpio_extcon_data *data) | ||
| 66 | { | ||
| 67 | struct gpio_extcon_pdata *pdata = data->pdata; | ||
| 68 | int ret; | ||
| 69 | |||
| 70 | ret = devm_gpio_request_one(dev, pdata->gpio, GPIOF_DIR_IN, | ||
| 71 | dev_name(dev)); | ||
| 72 | if (ret < 0) | ||
| 73 | return ret; | ||
| 74 | |||
| 75 | data->id_gpiod = gpio_to_desc(pdata->gpio); | ||
| 76 | if (!data->id_gpiod) | ||
| 77 | return -EINVAL; | ||
| 78 | |||
| 79 | if (pdata->debounce) { | ||
| 80 | ret = gpiod_set_debounce(data->id_gpiod, | ||
| 81 | pdata->debounce * 1000); | ||
| 82 | if (ret < 0) | ||
| 83 | data->debounce_jiffies = | ||
| 84 | msecs_to_jiffies(pdata->debounce); | ||
| 85 | } | ||
| 86 | |||
| 87 | data->irq = gpiod_to_irq(data->id_gpiod); | ||
| 88 | if (data->irq < 0) | ||
| 89 | return data->irq; | ||
| 90 | |||
| 91 | return 0; | ||
| 92 | } | ||
| 93 | |||
| 94 | static int gpio_extcon_probe(struct platform_device *pdev) | 76 | static int gpio_extcon_probe(struct platform_device *pdev) |
| 95 | { | 77 | { |
| 96 | struct gpio_extcon_pdata *pdata = dev_get_platdata(&pdev->dev); | ||
| 97 | struct gpio_extcon_data *data; | 78 | struct gpio_extcon_data *data; |
| 79 | struct device *dev = &pdev->dev; | ||
| 98 | int ret; | 80 | int ret; |
| 99 | 81 | ||
| 100 | if (!pdata) | 82 | data = devm_kzalloc(dev, sizeof(struct gpio_extcon_data), GFP_KERNEL); |
| 101 | return -EBUSY; | ||
| 102 | if (!pdata->irq_flags || pdata->extcon_id > EXTCON_NONE) | ||
| 103 | return -EINVAL; | ||
| 104 | |||
| 105 | data = devm_kzalloc(&pdev->dev, sizeof(struct gpio_extcon_data), | ||
| 106 | GFP_KERNEL); | ||
| 107 | if (!data) | 83 | if (!data) |
| 108 | return -ENOMEM; | 84 | return -ENOMEM; |
| 109 | data->pdata = pdata; | ||
| 110 | 85 | ||
| 111 | /* Initialize the gpio */ | 86 | /* |
| 112 | ret = gpio_extcon_init(&pdev->dev, data); | 87 | * FIXME: extcon_id represents the unique identifier of external |
| 113 | if (ret < 0) | 88 | * connectors such as EXTCON_USB, EXTCON_DISP_HDMI and so on. extcon_id |
| 114 | return ret; | 89 | * is necessary to register the extcon device. But, it's not yet |
| 90 | * developed to get the extcon id from device-tree or others. | ||
| 91 | * On later, it have to be solved. | ||
| 92 | */ | ||
| 93 | if (!data->irq_flags || data->extcon_id > EXTCON_NONE) | ||
| 94 | return -EINVAL; | ||
| 95 | |||
| 96 | data->gpiod = devm_gpiod_get(dev, "extcon", GPIOD_IN); | ||
| 97 | if (IS_ERR(data->gpiod)) | ||
| 98 | return PTR_ERR(data->gpiod); | ||
| 99 | data->irq = gpiod_to_irq(data->gpiod); | ||
| 100 | if (data->irq <= 0) | ||
| 101 | return data->irq; | ||
| 115 | 102 | ||
| 116 | /* Allocate the memory of extcon devie and register extcon device */ | 103 | /* Allocate the memory of extcon devie and register extcon device */ |
| 117 | data->edev = devm_extcon_dev_allocate(&pdev->dev, &pdata->extcon_id); | 104 | data->edev = devm_extcon_dev_allocate(dev, &data->extcon_id); |
| 118 | if (IS_ERR(data->edev)) { | 105 | if (IS_ERR(data->edev)) { |
| 119 | dev_err(&pdev->dev, "failed to allocate extcon device\n"); | 106 | dev_err(dev, "failed to allocate extcon device\n"); |
| 120 | return -ENOMEM; | 107 | return -ENOMEM; |
| 121 | } | 108 | } |
| 122 | 109 | ||
| 123 | ret = devm_extcon_dev_register(&pdev->dev, data->edev); | 110 | ret = devm_extcon_dev_register(dev, data->edev); |
| 124 | if (ret < 0) | 111 | if (ret < 0) |
| 125 | return ret; | 112 | return ret; |
| 126 | 113 | ||
| @@ -130,8 +117,8 @@ static int gpio_extcon_probe(struct platform_device *pdev) · 3 unmodified lines | |||
| 130 | * Request the interrupt of gpio to detect whether external connector | 117 | * Request the interrupt of gpio to detect whether external connector |
| 131 | * is attached or detached. | 118 | * is attached or detached. |
| 132 | */ | 119 | */ |
| 133 | ret = devm_request_any_context_irq(&pdev->dev, data->irq, | 120 | ret = devm_request_any_context_irq(dev, data->irq, |
| 134 | gpio_irq_handler, pdata->irq_flags, | 121 | gpio_irq_handler, data->irq_flags, |
| 135 | pdev->name, data); | 122 | pdev->name, data); |
| 136 | if (ret < 0) | 123 | if (ret < 0) |
| 137 | return ret; | 124 | return ret; |
| @@ -158,7 +145,7 @@ static int gpio_extcon_resume(struct device *dev) · 20 unmodified lines | |||
| 158 | struct gpio_extcon_data *data; | 145 | struct gpio_extcon_data *data; |
| 159 | 146 | ||
| 160 | data = dev_get_drvdata(dev); | 147 | data = dev_get_drvdata(dev); |
| 161 | if (data->pdata->check_on_resume) | 148 | if (data->check_on_resume) |
| 162 | queue_delayed_work(system_power_efficient_wq, | 149 | queue_delayed_work(system_power_efficient_wq, |
| 163 | &data->work, data->debounce_jiffies); | 150 | &data->work, data->debounce_jiffies); |
| 164 | 151 | ||
| @@ -66,6 +66,8 @@ · 65 unmodified lines | |||
| 66 | 66 | ||
| 67 | #define CHT_WC_VBUS_GPIO_CTLO 0x6e2d | 67 | #define CHT_WC_VBUS_GPIO_CTLO 0x6e2d |
| 68 | #define CHT_WC_VBUS_GPIO_CTLO_OUTPUT BIT(0) | 68 | #define CHT_WC_VBUS_GPIO_CTLO_OUTPUT BIT(0) |
| 69 | #define CHT_WC_VBUS_GPIO_CTLO_DRV_OD BIT(4) | ||
| 70 | #define CHT_WC_VBUS_GPIO_CTLO_DIR_OUT BIT(5) | ||
| 69 | 71 | ||
| 70 | enum cht_wc_usb_id { | 72 | enum cht_wc_usb_id { |
| 71 | USB_ID_OTG, | 73 | USB_ID_OTG, |
| @@ -183,14 +185,15 @@ static void cht_wc_extcon_set_5v_boost(struct cht_wc_extcon_data *ext, · 111 unmodified lines | |||
| 183 | { | 185 | { |
| 184 | int ret, val; | 186 | int ret, val; |
| 185 | 187 | ||
| 186 | val = enable ? CHT_WC_VBUS_GPIO_CTLO_OUTPUT : 0; | ||
| 187 | |||
| 188 | /* | 188 | /* |
| 189 | * The 5V boost converter is enabled through a gpio on the PMIC, since | 189 | * The 5V boost converter is enabled through a gpio on the PMIC, since |
| 190 | * there currently is no gpio driver we access the gpio reg directly. | 190 | * there currently is no gpio driver we access the gpio reg directly. |
| 191 | */ | 191 | */ |
| 192 | ret = regmap_update_bits(ext->regmap, CHT_WC_VBUS_GPIO_CTLO, | 192 | val = CHT_WC_VBUS_GPIO_CTLO_DRV_OD | CHT_WC_VBUS_GPIO_CTLO_DIR_OUT; |
| 193 | CHT_WC_VBUS_GPIO_CTLO_OUTPUT, val); | 193 | if (enable) |
| 194 | val |= CHT_WC_VBUS_GPIO_CTLO_OUTPUT; | ||
| 195 | |||
| 196 | ret = regmap_write(ext->regmap, CHT_WC_VBUS_GPIO_CTLO, val); | ||
| 194 | if (ret) | 197 | if (ret) |
| 195 | dev_err(ext->dev, "Error writing Vbus GPIO CTLO: %d\n", ret); | 198 | dev_err(ext->dev, "Error writing Vbus GPIO CTLO: %d\n", ret); |
| 196 | } | 199 | } |
| @@ -50,7 +50,11 @@ static const struct acpi_gpio_params vbus_gpios = { INT3496_GPIO_VBUS_EN, 0, fal · 49 unmodified lines | |||
| 50 | static const struct acpi_gpio_params mux_gpios = { INT3496_GPIO_USB_MUX, 0, false }; | 50 | static const struct acpi_gpio_params mux_gpios = { INT3496_GPIO_USB_MUX, 0, false }; |
| 51 | 51 | ||
| 52 | static const struct acpi_gpio_mapping acpi_int3496_default_gpios[] = { | 52 | static const struct acpi_gpio_mapping acpi_int3496_default_gpios[] = { |
| 53 | { "id-gpios", &id_gpios, 1 }, | 53 | /* |
| 54 | * Some platforms have a bug in ACPI GPIO description making IRQ | ||
| 55 | * GPIO to be output only. Ask the GPIO core to ignore this limit. | ||
| 56 | */ | ||
| 57 | { "id-gpios", &id_gpios, 1, ACPI_GPIO_QUIRK_NO_IO_RESTRICTION }, | ||
| 54 | { "vbus-gpios", &vbus_gpios, 1 }, | 58 | { "vbus-gpios", &vbus_gpios, 1 }, |
| 55 | { "mux-gpios", &mux_gpios, 1 }, | 59 | { "mux-gpios", &mux_gpios, 1 }, |
| 56 | { }, | 60 | { }, |
| @@ -112,9 +116,6 @@ static int int3496_probe(struct platform_device *pdev) · 55 unmodified lines | |||
| 112 | ret = PTR_ERR(data->gpio_usb_id); | 116 | ret = PTR_ERR(data->gpio_usb_id); |
| 113 | dev_err(dev, "can't request USB ID GPIO: %d\n", ret); | 117 | dev_err(dev, "can't request USB ID GPIO: %d\n", ret); |
| 114 | return ret; | 118 | return ret; |
| 115 | } else if (gpiod_get_direction(data->gpio_usb_id) != GPIOF_DIR_IN) { | ||
| 116 | dev_warn(dev, FW_BUG "USB ID GPIO not in input mode, fixing\n"); | ||
| 117 | gpiod_direction_input(data->gpio_usb_id); | ||
| 118 | } | 119 | } |
| 119 | 120 | ||
| 120 | data->usb_id_irq = gpiod_to_irq(data->gpio_usb_id); | 121 | data->usb_id_irq = gpiod_to_irq(data->gpio_usb_id); |
| @@ -1336,6 +1336,28 @@ void extcon_dev_unregister(struct extcon_dev *edev) · 1335 unmodified lines | |||
| 1336 | EXPORT_SYMBOL_GPL(extcon_dev_unregister); | 1336 | EXPORT_SYMBOL_GPL(extcon_dev_unregister); |
| 1337 | 1337 | ||
| 1338 | #ifdef CONFIG_OF | 1338 | #ifdef CONFIG_OF |
| 1339 | |||
| 1340 | /* | ||
| 1341 | * extcon_find_edev_by_node - Find the extcon device from devicetree. | ||
| 1342 | * @node : OF node identifying edev | ||
| 1343 | * | ||
| 1344 | * Return the pointer of extcon device if success or ERR_PTR(err) if fail. | ||
| 1345 | */ | ||
| 1346 | struct extcon_dev *extcon_find_edev_by_node(struct device_node *node) | ||
| 1347 | { | ||
| 1348 | struct extcon_dev *edev; | ||
| 1349 | |||
| 1350 | mutex_lock(&extcon_dev_list_lock); | ||
| 1351 | list_for_each_entry(edev, &extcon_dev_list, entry) | ||
| 1352 | if (edev->dev.parent && edev->dev.parent->of_node == node) | ||
| 1353 | goto out; | ||
| 1354 | edev = ERR_PTR(-EPROBE_DEFER); | ||
| 1355 | out: | ||
| 1356 | mutex_unlock(&extcon_dev_list_lock); | ||
| 1357 | |||
| 1358 | return edev; | ||
| 1359 | } | ||
| 1360 | |||
| 1339 | /* | 1361 | /* |
| 1340 | * extcon_get_edev_by_phandle - Get the extcon device from devicetree. | 1362 | * extcon_get_edev_by_phandle - Get the extcon device from devicetree. |
| 1341 | * @dev : the instance to the given device | 1363 | * @dev : the instance to the given device |
| @@ -1363,25 +1385,27 @@ struct extcon_dev *extcon_get_edev_by_phandle(struct device *dev, int index) · 21 unmodified lines | |||
| 1363 | return ERR_PTR(-ENODEV); | 1385 | return ERR_PTR(-ENODEV); |
| 1364 | } | 1386 | } |
| 1365 | 1387 | ||
| 1366 | mutex_lock(&extcon_dev_list_lock); | 1388 | edev = extcon_find_edev_by_node(node); |
| 1367 | list_for_each_entry(edev, &extcon_dev_list, entry) { | ||
| 1368 | if (edev->dev.parent && edev->dev.parent->of_node == node) { | ||
| 1369 | mutex_unlock(&extcon_dev_list_lock); | ||
| 1370 | of_node_put(node); | ||
| 1371 | return edev; | ||
| 1372 | } | ||
| 1373 | } | ||
| 1374 | mutex_unlock(&extcon_dev_list_lock); | ||
| 1375 | of_node_put(node); | 1389 | of_node_put(node); |
| 1376 | 1390 | ||
| 1377 | return ERR_PTR(-EPROBE_DEFER); | 1391 | return edev; |
| 1378 | } | 1392 | } |
| 1393 | |||
| 1379 | #else | 1394 | #else |
| 1395 | |||
| 1396 | struct extcon_dev *extcon_find_edev_by_node(struct device_node *node) | ||
| 1397 | { | ||
| 1398 | return ERR_PTR(-ENOSYS); | ||
| 1399 | } | ||
| 1400 | |||
| 1380 | struct extcon_dev *extcon_get_edev_by_phandle(struct device *dev, int index) | 1401 | struct extcon_dev *extcon_get_edev_by_phandle(struct device *dev, int index) |
| 1381 | { | 1402 | { |
| 1382 | return ERR_PTR(-ENOSYS); | 1403 | return ERR_PTR(-ENOSYS); |
| 1383 | } | 1404 | } |
| 1405 | |||
| 1384 | #endif /* CONFIG_OF */ | 1406 | #endif /* CONFIG_OF */ |
| 1407 | |||
| 1408 | EXPORT_SYMBOL_GPL(extcon_find_edev_by_node); | ||
| 1385 | EXPORT_SYMBOL_GPL(extcon_get_edev_by_phandle); | 1409 | EXPORT_SYMBOL_GPL(extcon_get_edev_by_phandle); |
| 1386 | 1410 | ||
| 1387 | /** | 1411 | /** |
| @@ -384,8 +384,6 @@ static int altera_cvp_probe(struct pci_dev *pdev, · 383 unmodified lines | |||
| 384 | const struct pci_device_id *dev_id); | 384 | const struct pci_device_id *dev_id); |
| 385 | static void altera_cvp_remove(struct pci_dev *pdev); | 385 | static void altera_cvp_remove(struct pci_dev *pdev); |
| 386 | 386 | ||
| 387 | #define PCI_VENDOR_ID_ALTERA 0x1172 | ||
| 388 | |||
| 389 | static struct pci_device_id altera_cvp_id_tbl[] = { | 387 | static struct pci_device_id altera_cvp_id_tbl[] = { |
| 390 | { PCI_VDEVICE(ALTERA, PCI_ANY_ID) }, | 388 | { PCI_VDEVICE(ALTERA, PCI_ANY_ID) }, |
| 391 | { } | 389 | { } |
| @@ -4,6 +4,7 @@ · 3 unmodified lines | |||
| 4 | 4 | ||
| 5 | menuconfig FSI | 5 | menuconfig FSI |
| 6 | tristate "FSI support" | 6 | tristate "FSI support" |
| 7 | depends on OF | ||
| 7 | select CRC4 | 8 | select CRC4 |
| 8 | ---help--- | 9 | ---help--- |
| 9 | FSI - the FRU Support Interface - is a simple bus for low-level | 10 | FSI - the FRU Support Interface - is a simple bus for low-level |
| @@ -18,6 +18,7 @@ · 17 unmodified lines | |||
| 18 | #include <linux/fsi.h> | 18 | #include <linux/fsi.h> |
| 19 | #include <linux/idr.h> | 19 | #include <linux/idr.h> |
| 20 | #include <linux/module.h> | 20 | #include <linux/module.h> |
| 21 | #include <linux/of.h> | ||
| 21 | #include <linux/slab.h> | 22 | #include <linux/slab.h> |
| 22 | #include <linux/bitops.h> | 23 | #include <linux/bitops.h> |
| 23 | 24 | ||
| @@ -142,6 +143,7 @@ static void fsi_device_release(struct device *_device) · 118 unmodified lines | |||
| 142 | { | 143 | { |
| 143 | struct fsi_device *device = to_fsi_dev(_device); | 144 | struct fsi_device *device = to_fsi_dev(_device); |
| 144 | 145 | ||
| 146 | of_node_put(device->dev.of_node); | ||
| 145 | kfree(device); | 147 | kfree(device); |
| 146 | } | 148 | } |
| 147 | 149 | ||
| @@ -205,7 +207,7 @@ static int fsi_slave_report_and_clear_errors(struct fsi_slave *slave) · 57 unmodified lines | |||
| 205 | if (rc) | 207 | if (rc) |
| 206 | return rc; | 208 | return rc; |
| 207 | 209 | ||
| 208 | dev_info(&slave->dev, "status: 0x%08x, sisc: 0x%08x\n", | 210 | dev_dbg(&slave->dev, "status: 0x%08x, sisc: 0x%08x\n", |
| 209 | be32_to_cpu(stat), be32_to_cpu(irq)); | 211 | be32_to_cpu(stat), be32_to_cpu(irq)); |
| 210 | 212 | ||
| 211 | /* clear interrupts */ | 213 | /* clear interrupts */ |
| @@ -334,6 +336,57 @@ extern void fsi_slave_release_range(struct fsi_slave *slave, · 122 unmodified lines | |||
| 334 | } | 336 | } |
| 335 | EXPORT_SYMBOL_GPL(fsi_slave_release_range); | 337 | EXPORT_SYMBOL_GPL(fsi_slave_release_range); |
| 336 | 338 | ||
| 339 | static bool fsi_device_node_matches(struct device *dev, struct device_node *np, | ||
| 340 | uint32_t addr, uint32_t size) | ||
| 341 | { | ||
| 342 | unsigned int len, na, ns; | ||
| 343 | const __be32 *prop; | ||
| 344 | uint32_t psize; | ||
| 345 | |||
| 346 | na = of_n_addr_cells(np); | ||
| 347 | ns = of_n_size_cells(np); | ||
| 348 | |||
| 349 | if (na != 1 || ns != 1) | ||
| 350 | return false; | ||
| 351 | |||
| 352 | prop = of_get_property(np, "reg", &len); | ||
| 353 | if (!prop || len != 8) | ||
| 354 | return false; | ||
| 355 | |||
| 356 | if (of_read_number(prop, 1) != addr) | ||
| 357 | return false; | ||
| 358 | |||
| 359 | psize = of_read_number(prop + 1, 1); | ||
| 360 | if (psize != size) { | ||
| 361 | dev_warn(dev, | ||
| 362 | "node %s matches probed address, but not size (got 0x%x, expected 0x%x)", | ||
| 363 | of_node_full_name(np), psize, size); | ||
| 364 | } | ||
| 365 | |||
| 366 | return true; | ||
| 367 | } | ||
| 368 | |||
| 369 | /* Find a matching node for the slave engine at @address, using @size bytes | ||
| 370 | * of space. Returns NULL if not found, or a matching node with refcount | ||
| 371 | * already incremented. | ||
| 372 | */ | ||
| 373 | static struct device_node *fsi_device_find_of_node(struct fsi_device *dev) | ||
| 374 | { | ||
| 375 | struct device_node *parent, *np; | ||
| 376 | |||
| 377 | parent = dev_of_node(&dev->slave->dev); | ||
| 378 | if (!parent) | ||
| 379 | return NULL; | ||
| 380 | |||
| 381 | for_each_child_of_node(parent, np) { | ||
| 382 | if (fsi_device_node_matches(&dev->dev, np, | ||
| 383 | dev->addr, dev->size)) | ||
| 384 | return np; | ||
| 385 | } | ||
| 386 | |||
| 387 | return NULL; | ||
| 388 | } | ||
| 389 | |||
| 337 | static int fsi_slave_scan(struct fsi_slave *slave) | 390 | static int fsi_slave_scan(struct fsi_slave *slave) |
| 338 | { | 391 | { |
| 339 | uint32_t engine_addr; | 392 | uint32_t engine_addr; |
| @@ -402,6 +455,7 @@ static int fsi_slave_scan(struct fsi_slave *slave) · 62 unmodified lines | |||
| 402 | dev_set_name(&dev->dev, "%02x:%02x:%02x:%02x", | 455 | dev_set_name(&dev->dev, "%02x:%02x:%02x:%02x", |
| 403 | slave->master->idx, slave->link, | 456 | slave->master->idx, slave->link, |
| 404 | slave->id, i - 2); | 457 | slave->id, i - 2); |
| 458 | dev->dev.of_node = fsi_device_find_of_node(dev); | ||
| 405 | 459 | ||
| 406 | rc = device_register(&dev->dev); | 460 | rc = device_register(&dev->dev); |
| 407 | if (rc) { | 461 | if (rc) { |
| @@ -558,9 +612,53 @@ static void fsi_slave_release(struct device *dev) · 150 unmodified lines | |||
| 558 | { | 612 | { |
| 559 | struct fsi_slave *slave = to_fsi_slave(dev); | 613 | struct fsi_slave *slave = to_fsi_slave(dev); |
| 560 | 614 | ||
| 615 | of_node_put(dev->of_node); | ||
| 561 | kfree(slave); | 616 | kfree(slave); |
| 562 | } | 617 | } |
| 563 | 618 | ||
| 619 | static bool fsi_slave_node_matches(struct device_node *np, | ||
| 620 | int link, uint8_t id) | ||
| 621 | { | ||
| 622 | unsigned int len, na, ns; | ||
| 623 | const __be32 *prop; | ||
| 624 | |||
| 625 | na = of_n_addr_cells(np); | ||
| 626 | ns = of_n_size_cells(np); | ||
| 627 | |||
| 628 | /* Ensure we have the correct format for addresses and sizes in | ||
| 629 | * reg properties | ||
| 630 | */ | ||
| 631 | if (na != 2 || ns != 0) | ||
| 632 | return false; | ||
| 633 | |||
| 634 | prop = of_get_property(np, "reg", &len); | ||
| 635 | if (!prop || len != 8) | ||
| 636 | return false; | ||
| 637 | |||
| 638 | return (of_read_number(prop, 1) == link) && | ||
| 639 | (of_read_number(prop + 1, 1) == id); | ||
| 640 | } | ||
| 641 | |||
| 642 | /* Find a matching node for the slave at (link, id). Returns NULL if none | ||
| 643 | * found, or a matching node with refcount already incremented. | ||
| 644 | */ | ||
| 645 | static struct device_node *fsi_slave_find_of_node(struct fsi_master *master, | ||
| 646 | int link, uint8_t id) | ||
| 647 | { | ||
| 648 | struct device_node *parent, *np; | ||
| 649 | |||
| 650 | parent = dev_of_node(&master->dev); | ||
| 651 | if (!parent) | ||
| 652 | return NULL; | ||
| 653 | |||
| 654 | for_each_child_of_node(parent, np) { | ||
| 655 | if (fsi_slave_node_matches(np, link, id)) | ||
| 656 | return np; | ||
| 657 | } | ||
| 658 | |||
| 659 | return NULL; | ||
| 660 | } | ||
| 661 | |||
| 564 | static int fsi_slave_init(struct fsi_master *master, int link, uint8_t id) | 662 | static int fsi_slave_init(struct fsi_master *master, int link, uint8_t id) |
| 565 | { | 663 | { |
| 566 | uint32_t chip_id, llmode; | 664 | uint32_t chip_id, llmode; |
| @@ -589,7 +687,7 @@ static int fsi_slave_init(struct fsi_master *master, int link, uint8_t id) · 22 unmodified lines | |||
| 589 | return -EIO; | 687 | return -EIO; |
| 590 | } | 688 | } |
| 591 | 689 | ||
| 592 | dev_info(&master->dev, "fsi: found chip %08x at %02x:%02x:%02x\n", | 690 | dev_dbg(&master->dev, "fsi: found chip %08x at %02x:%02x:%02x\n", |
| 593 | chip_id, master->idx, link, id); | 691 | chip_id, master->idx, link, id); |
| 594 | 692 | ||
| 595 | rc = fsi_slave_set_smode(master, link, id); | 693 | rc = fsi_slave_set_smode(master, link, id); |
| @@ -623,6 +721,7 @@ static int fsi_slave_init(struct fsi_master *master, int link, uint8_t id) · 27 unmodified lines | |||
| 623 | 721 | ||
| 624 | slave->master = master; | 722 | slave->master = master; |
| 625 | slave->dev.parent = &master->dev; | 723 | slave->dev.parent = &master->dev; |
| 724 | slave->dev.of_node = fsi_slave_find_of_node(master, link, id); | ||
| 626 | slave->dev.release = fsi_slave_release; | 725 | slave->dev.release = fsi_slave_release; |
| 627 | slave->link = link; | 726 | slave->link = link; |
| 628 | slave->id = id; | 727 | slave->id = id; |
| @@ -656,10 +755,13 @@ static int fsi_slave_init(struct fsi_master *master, int link, uint8_t id) · 27 unmodified lines | |||
| 656 | /* FSI master support */ | 755 | /* FSI master support */ |
| 657 | static int fsi_check_access(uint32_t addr, size_t size) | 756 | static int fsi_check_access(uint32_t addr, size_t size) |
| 658 | { | 757 | { |
| 659 | if (size != 1 && size != 2 && size != 4) | 758 | if (size == 4) { |
| 660 | return -EINVAL; | 759 | if (addr & 0x3) |
| 661 | 760 | return -EINVAL; | |
| 662 | if ((addr & 0x3) != (size & 0x3)) | 761 | } else if (size == 2) { |
| 762 | if (addr & 0x1) | ||
| 763 | return -EINVAL; | ||
| 764 | } else if (size != 1) | ||
| 663 | return -EINVAL; | 765 | return -EINVAL; |
| 664 | 766 | ||
| 665 | return 0; | 767 | return 0; |
| @@ -762,14 +864,20 @@ static void fsi_master_unscan(struct fsi_master *master) · 96 unmodified lines | |||
| 762 | device_for_each_child(&master->dev, NULL, fsi_master_remove_slave); | 864 | device_for_each_child(&master->dev, NULL, fsi_master_remove_slave); |
| 763 | } | 865 | } |
| 764 | 866 | ||
| 867 | int fsi_master_rescan(struct fsi_master *master) | ||
| 868 | { | ||
| 869 | fsi_master_unscan(master); | ||
| 870 | return fsi_master_scan(master); | ||
| 871 | } | ||
| 872 | EXPORT_SYMBOL_GPL(fsi_master_rescan); | ||
| 873 | |||
| 765 | static ssize_t master_rescan_store(struct device *dev, | 874 | static ssize_t master_rescan_store(struct device *dev, |
| 766 | struct device_attribute *attr, const char *buf, size_t count) | 875 | struct device_attribute *attr, const char *buf, size_t count) |
| 767 | { | 876 | { |
| 768 | struct fsi_master *master = to_fsi_master(dev); | 877 | struct fsi_master *master = to_fsi_master(dev); |
| 769 | int rc; | 878 | int rc; |
| 770 | 879 | ||
| 771 | fsi_master_unscan(master); | 880 | rc = fsi_master_rescan(master); |
| 772 | rc = fsi_master_scan(master); | ||
| 773 | if (rc < 0) | 881 | if (rc < 0) |
| 774 | return rc; | 882 | return rc; |
| 775 | 883 | ||
| @@ -793,6 +901,7 @@ static DEVICE_ATTR(break, 0200, NULL, master_break_store); · 17 unmodified lines | |||
| 793 | int fsi_master_register(struct fsi_master *master) | 901 | int fsi_master_register(struct fsi_master *master) |
| 794 | { | 902 | { |
| 795 | int rc; | 903 | int rc; |
| 904 | struct device_node *np; | ||
| 796 | 905 | ||
| 797 | if (!master) | 906 | if (!master) |
| 798 | return -EINVAL; | 907 | return -EINVAL; |
| @@ -820,7 +929,9 @@ int fsi_master_register(struct fsi_master *master) · 21 unmodified lines | |||
| 820 | return rc; | 929 | return rc; |
| 821 | } | 930 | } |
| 822 | 931 | ||
| 823 | fsi_master_scan(master); | 932 | np = dev_of_node(&master->dev); |
| 933 | if (!of_property_read_bool(np, "no-scan-on-init")) | ||
| 934 | fsi_master_scan(master); | ||
| 824 | 935 | ||
| 825 | return 0; | 936 | return 0; |
| 826 | } | 937 | } |
| @@ -9,6 +9,7 @@ · 8 unmodified lines | |||
| 9 | #include <linux/gpio/consumer.h> | 9 | #include <linux/gpio/consumer.h> |
| 10 | #include <linux/io.h> | 10 | #include <linux/io.h> |
| 11 | #include <linux/module.h> | 11 | #include <linux/module.h> |
| 12 | #include <linux/of.h> | ||
| 12 | #include <linux/platform_device.h> | 13 | #include <linux/platform_device.h> |
| 13 | #include <linux/slab.h> | 14 | #include <linux/slab.h> |
| 14 | #include <linux/spinlock.h> | 15 | #include <linux/spinlock.h> |
| @@ -59,6 +60,7 @@ struct fsi_master_gpio { · 44 unmodified lines | |||
| 59 | struct gpio_desc *gpio_trans; /* Voltage translator */ | 60 | struct gpio_desc *gpio_trans; /* Voltage translator */ |
| 60 | struct gpio_desc *gpio_enable; /* FSI enable */ | 61 | struct gpio_desc *gpio_enable; /* FSI enable */ |
| 61 | struct gpio_desc *gpio_mux; /* Mux control */ | 62 | struct gpio_desc *gpio_mux; /* Mux control */ |
| 63 | bool external_mode; | ||
| 62 | }; | 64 | }; |
| 63 | 65 | ||
| 64 | #define CREATE_TRACE_POINTS | 66 | #define CREATE_TRACE_POINTS |
| @@ -411,6 +413,12 @@ static int fsi_master_gpio_xfer(struct fsi_master_gpio *master, uint8_t slave, · 346 unmodified lines | |||
| 411 | int rc; | 413 | int rc; |
| 412 | 414 | ||
| 413 | spin_lock_irqsave(&master->cmd_lock, flags); | 415 | spin_lock_irqsave(&master->cmd_lock, flags); |
| 416 | |||
| 417 | if (master->external_mode) { | ||
| 418 | spin_unlock_irqrestore(&master->cmd_lock, flags); | ||
| 419 | return -EBUSY; | ||
| 420 | } | ||
| 421 | |||
| 414 | serial_out(master, cmd); | 422 | serial_out(master, cmd); |
| 415 | echo_delay(master); | 423 | echo_delay(master); |
| 416 | rc = poll_for_response(master, slave, resp_len, resp); | 424 | rc = poll_for_response(master, slave, resp_len, resp); |
| @@ -461,12 +469,18 @@ static int fsi_master_gpio_term(struct fsi_master *_master, · 44 unmodified lines | |||
| 461 | static int fsi_master_gpio_break(struct fsi_master *_master, int link) | 469 | static int fsi_master_gpio_break(struct fsi_master *_master, int link) |
| 462 | { | 470 | { |
| 463 | struct fsi_master_gpio *master = to_fsi_master_gpio(_master); | 471 | struct fsi_master_gpio *master = to_fsi_master_gpio(_master); |
| 472 | unsigned long flags; | ||
| 464 | 473 | ||
| 465 | if (link != 0) | 474 | if (link != 0) |
| 466 | return -ENODEV; | 475 | return -ENODEV; |
| 467 | 476 | ||
| 468 | trace_fsi_master_gpio_break(master); | 477 | trace_fsi_master_gpio_break(master); |
| 469 | 478 | ||
| 479 | spin_lock_irqsave(&master->cmd_lock, flags); | ||
| 480 | if (master->external_mode) { | ||
| 481 | spin_unlock_irqrestore(&master->cmd_lock, flags); | ||
| 482 | return -EBUSY; | ||
| 483 | } | ||
| 470 | set_sda_output(master, 1); | 484 | set_sda_output(master, 1); |
| 471 | sda_out(master, 1); | 485 | sda_out(master, 1); |
| 472 | clock_toggle(master, FSI_PRE_BREAK_CLOCKS); | 486 | clock_toggle(master, FSI_PRE_BREAK_CLOCKS); |
| @@ -475,6 +489,7 @@ static int fsi_master_gpio_break(struct fsi_master *_master, int link) · 2 unmodified lines | |||
| 475 | echo_delay(master); | 489 | echo_delay(master); |
| 476 | sda_out(master, 1); | 490 | sda_out(master, 1); |
| 477 | clock_toggle(master, FSI_POST_BREAK_CLOCKS); | 491 | clock_toggle(master, FSI_POST_BREAK_CLOCKS); |
| 492 | spin_unlock_irqrestore(&master->cmd_lock, flags); | ||
| 478 | 493 | ||
| 479 | /* Wait for logic reset to take effect */ | 494 | /* Wait for logic reset to take effect */ |
| 480 | udelay(200); | 495 | udelay(200); |
| @@ -494,21 +509,84 @@ static void fsi_master_gpio_init(struct fsi_master_gpio *master) · 13 unmodified lines | |||
| 494 | clock_zeros(master, FSI_INIT_CLOCKS); | 509 | clock_zeros(master, FSI_INIT_CLOCKS); |
| 495 | } | 510 | } |
| 496 | 511 | ||
| 512 | static void fsi_master_gpio_init_external(struct fsi_master_gpio *master) | ||
| 513 | { | ||
| 514 | gpiod_direction_output(master->gpio_mux, 0); | ||
| 515 | gpiod_direction_output(master->gpio_trans, 0); | ||
| 516 | gpiod_direction_output(master->gpio_enable, 1); | ||
| 517 | gpiod_direction_input(master->gpio_clk); | ||
| 518 | gpiod_direction_input(master->gpio_data); | ||
| 519 | } | ||
| 520 | |||
| 497 | static int fsi_master_gpio_link_enable(struct fsi_master *_master, int link) | 521 | static int fsi_master_gpio_link_enable(struct fsi_master *_master, int link) |
| 498 | { | 522 | { |
| 499 | struct fsi_master_gpio *master = to_fsi_master_gpio(_master); | 523 | struct fsi_master_gpio *master = to_fsi_master_gpio(_master); |
| 524 | unsigned long flags; | ||
| 525 | int rc = -EBUSY; | ||
| 500 | 526 | ||
| 501 | if (link != 0) | 527 | if (link != 0) |
| 502 | return -ENODEV; | 528 | return -ENODEV; |
| 503 | gpiod_set_value(master->gpio_enable, 1); | ||
| 504 | 529 | ||
| 505 | return 0; | 530 | spin_lock_irqsave(&master->cmd_lock, flags); |
| 531 | if (!master->external_mode) { | ||
| 532 | gpiod_set_value(master->gpio_enable, 1); | ||
| 533 | rc = 0; | ||
| 534 | } | ||
| 535 | spin_unlock_irqrestore(&master->cmd_lock, flags); | ||
| 536 | |||
| 537 | return rc; | ||
| 538 | } | ||
| 539 | |||
| 540 | static ssize_t external_mode_show(struct device *dev, | ||
| 541 | struct device_attribute *attr, char *buf) | ||
| 542 | { | ||
| 543 | struct fsi_master_gpio *master = dev_get_drvdata(dev); | ||
| 544 | |||
| 545 | return snprintf(buf, PAGE_SIZE - 1, "%u\n", | ||
| 546 | master->external_mode ? 1 : 0); | ||
| 506 | } | 547 | } |
| 507 | 548 | ||
| 549 | static ssize_t external_mode_store(struct device *dev, | ||
| 550 | struct device_attribute *attr, const char *buf, size_t count) | ||
| 551 | { | ||
| 552 | struct fsi_master_gpio *master = dev_get_drvdata(dev); | ||
| 553 | unsigned long flags, val; | ||
| 554 | bool external_mode; | ||
| 555 | int err; | ||
| 556 | |||
| 557 | err = kstrtoul(buf, 0, &val); | ||
| 558 | if (err) | ||
| 559 | return err; | ||
| 560 | |||
| 561 | external_mode = !!val; | ||
| 562 | |||
| 563 | spin_lock_irqsave(&master->cmd_lock, flags); | ||
| 564 | |||
| 565 | if (external_mode == master->external_mode) { | ||
| 566 | spin_unlock_irqrestore(&master->cmd_lock, flags); | ||
| 567 | return count; | ||
| 568 | } | ||
| 569 | |||
| 570 | master->external_mode = external_mode; | ||
| 571 | if (master->external_mode) | ||
| 572 | fsi_master_gpio_init_external(master); | ||
| 573 | else | ||
| 574 | fsi_master_gpio_init(master); | ||
| 575 | spin_unlock_irqrestore(&master->cmd_lock, flags); | ||
| 576 | |||
| 577 | fsi_master_rescan(&master->master); | ||
| 578 | |||
| 579 | return count; | ||
| 580 | } | ||
| 581 | |||
| 582 | static DEVICE_ATTR(external_mode, 0664, | ||
| 583 | external_mode_show, external_mode_store); | ||
| 584 | |||
| 508 | static int fsi_master_gpio_probe(struct platform_device *pdev) | 585 | static int fsi_master_gpio_probe(struct platform_device *pdev) |
| 509 | { | 586 | { |
| 510 | struct fsi_master_gpio *master; | 587 | struct fsi_master_gpio *master; |
| 511 | struct gpio_desc *gpio; | 588 | struct gpio_desc *gpio; |
| 589 | int rc; | ||
| 512 | 590 | ||
| 513 | master = devm_kzalloc(&pdev->dev, sizeof(*master), GFP_KERNEL); | 591 | master = devm_kzalloc(&pdev->dev, sizeof(*master), GFP_KERNEL); |
| 514 | if (!master) | 592 | if (!master) |
| @@ -516,6 +594,7 @@ static int fsi_master_gpio_probe(struct platform_device *pdev) · 1 unmodified line | |||
| 516 | 594 | ||
| 517 | master->dev = &pdev->dev; | 595 | master->dev = &pdev->dev; |
| 518 | master->master.dev.parent = master->dev; | 596 | master->master.dev.parent = master->dev; |
| 597 | master->master.dev.of_node = of_node_get(dev_of_node(master->dev)); | ||
| 519 | 598 | ||
| 520 | gpio = devm_gpiod_get(&pdev->dev, "clock", 0); | 599 | gpio = devm_gpiod_get(&pdev->dev, "clock", 0); |
| 521 | if (IS_ERR(gpio)) { | 600 | if (IS_ERR(gpio)) { |
| @@ -565,6 +644,10 @@ static int fsi_master_gpio_probe(struct platform_device *pdev) · 43 unmodified lines | |||
| 565 | 644 | ||
| 566 | fsi_master_gpio_init(master); | 645 | fsi_master_gpio_init(master); |
| 567 | 646 | ||
| 647 | rc = device_create_file(&pdev->dev, &dev_attr_external_mode); | ||
| 648 | if (rc) | ||
| 649 | return rc; | ||
| 650 | |||
| 568 | return fsi_master_register(&master->master); | 651 | return fsi_master_register(&master->master); |
| 569 | } | 652 | } |
| 570 | 653 | ||
| @@ -583,6 +666,8 @@ static int fsi_master_gpio_remove(struct platform_device *pdev) · 12 unmodified lines | |||
| 583 | devm_gpiod_put(&pdev->dev, master->gpio_mux); | 666 | devm_gpiod_put(&pdev->dev, master->gpio_mux); |
| 584 | fsi_master_unregister(&master->master); | 667 | fsi_master_unregister(&master->master); |
| 585 | 668 | ||
| 669 | of_node_put(master->master.dev.of_node); | ||
| 670 | |||
| 586 | return 0; | 671 | return 0; |
| 587 | } | 672 | } |
| 588 | 673 | ||
| @@ -16,6 +16,7 @@ · 15 unmodified lines | |||
| 16 | #include <linux/delay.h> | 16 | #include <linux/delay.h> |
| 17 | #include <linux/fsi.h> | 17 | #include <linux/fsi.h> |
| 18 | #include <linux/module.h> | 18 | #include <linux/module.h> |
| 19 | #include <linux/of.h> | ||
| 19 | #include <linux/slab.h> | 20 | #include <linux/slab.h> |
| 20 | 21 | ||
| 21 | #include "fsi-master.h" | 22 | #include "fsi-master.h" |
| @@ -253,7 +254,7 @@ static int hub_master_probe(struct device *dev) · 231 unmodified lines | |||
| 253 | 254 | ||
| 254 | reg = be32_to_cpu(__reg); | 255 | reg = be32_to_cpu(__reg); |
| 255 | links = (reg >> 8) & 0xff; | 256 | links = (reg >> 8) & 0xff; |
| 256 | dev_info(dev, "hub version %08x (%d links)\n", reg, links); | 257 | dev_dbg(dev, "hub version %08x (%d links)\n", reg, links); |
| 257 | 258 | ||
| 258 | rc = fsi_slave_claim_range(fsi_dev->slave, FSI_HUB_LINK_OFFSET, | 259 | rc = fsi_slave_claim_range(fsi_dev->slave, FSI_HUB_LINK_OFFSET, |
| 259 | FSI_HUB_LINK_SIZE * links); | 260 | FSI_HUB_LINK_SIZE * links); |
| @@ -274,6 +275,7 @@ static int hub_master_probe(struct device *dev) · 14 unmodified lines | |||
| 274 | 275 | ||
| 275 | hub->master.dev.parent = dev; | 276 | hub->master.dev.parent = dev; |
| 276 | hub->master.dev.release = hub_master_release; | 277 | hub->master.dev.release = hub_master_release; |
| 278 | hub->master.dev.of_node = of_node_get(dev_of_node(dev)); | ||
| 277 | 279 | ||
| 278 | hub->master.n_links = links; | 280 | hub->master.n_links = links; |
| 279 | hub->master.read = hub_master_read; | 281 | hub->master.read = hub_master_read; |
| @@ -286,10 +288,19 @@ static int hub_master_probe(struct device *dev) · 6 unmodified lines | |||
| 286 | hub_master_init(hub); | 288 | hub_master_init(hub); |
| 287 | 289 | ||
| 288 | rc = fsi_master_register(&hub->master); | 290 | rc = fsi_master_register(&hub->master); |
| 289 | if (!rc) | 291 | if (rc) |
| 290 | return 0; | 292 | goto err_release; |
| 293 | |||
| 294 | /* At this point, fsi_master_register performs the device_initialize(), | ||
| 295 | * and holds the sole reference on master.dev. This means the device | ||
| 296 | * will be freed (via ->release) during any subsequent call to | ||
| 297 | * fsi_master_unregister. We add our own reference to it here, so we | ||
| 298 | * can perform cleanup (in _remove()) without it being freed before | ||
| 299 | * we're ready. | ||
| 300 | */ | ||
| 301 | get_device(&hub->master.dev); | ||
| 302 | return 0; | ||
| 291 | 303 | ||
| 292 | kfree(hub); | ||
| 293 | err_release: | 304 | err_release: |
| 294 | fsi_slave_release_range(fsi_dev->slave, FSI_HUB_LINK_OFFSET, | 305 | fsi_slave_release_range(fsi_dev->slave, FSI_HUB_LINK_OFFSET, |
| 295 | FSI_HUB_LINK_SIZE * links); | 306 | FSI_HUB_LINK_SIZE * links); |
| @@ -302,6 +313,14 @@ static int hub_master_remove(struct device *dev) · 6 unmodified lines | |||
| 302 | 313 | ||
| 303 | fsi_master_unregister(&hub->master); | 314 | fsi_master_unregister(&hub->master); |
| 304 | fsi_slave_release_range(hub->upstream->slave, hub->addr, hub->size); | 315 | fsi_slave_release_range(hub->upstream->slave, hub->addr, hub->size); |
| 316 | of_node_put(hub->master.dev.of_node); | ||
| 317 | |||
| 318 | /* | ||
| 319 | * master.dev will likely be ->release()ed after this, which free()s | ||
| 320 | * the hub | ||
| 321 | */ | ||
| 322 | put_device(&hub->master.dev); | ||
| 323 | |||
| 305 | return 0; | 324 | return 0; |
| 306 | } | 325 | } |
| 307 | 326 | ||
| @@ -37,7 +37,24 @@ struct fsi_master { · 36 unmodified lines | |||
| 37 | 37 | ||
| 38 | #define dev_to_fsi_master(d) container_of(d, struct fsi_master, dev) | 38 | #define dev_to_fsi_master(d) container_of(d, struct fsi_master, dev) |
| 39 | 39 | ||
| 40 | /** | ||
| 41 | * fsi_master registration & lifetime: the fsi_master_register() and | ||
| 42 | * fsi_master_unregister() functions will take ownership of the master, and | ||
| 43 | * ->dev in particular. The registration path performs a get_device(), which | ||
| 44 | * takes the first reference on the device. Similarly, the unregistration path | ||
| 45 | * performs a put_device(), which may well drop the last reference. | ||
| 46 | * | ||
| 47 | * This means that master implementations *may* need to hold their own | ||
| 48 | * reference (via get_device()) on master->dev. In particular, if the device's | ||
| 49 | * ->release callback frees the fsi_master, then fsi_master_unregister will | ||
| 50 | * invoke this free if no other reference is held. | ||
| 51 | * | ||
| 52 | * The same applies for the error path of fsi_master_register; if the call | ||
| 53 | * fails, dev->release will have been invoked. | ||
| 54 | */ | ||
| 40 | extern int fsi_master_register(struct fsi_master *master); | 55 | extern int fsi_master_register(struct fsi_master *master); |
| 41 | extern void fsi_master_unregister(struct fsi_master *master); | 56 | extern void fsi_master_unregister(struct fsi_master *master); |
| 42 | 57 | ||
| 58 | extern int fsi_master_rescan(struct fsi_master *master); | ||
| 59 | |||
| 43 | #endif /* DRIVERS_FSI_MASTER_H */ | 60 | #endif /* DRIVERS_FSI_MASTER_H */ |
| @@ -17,6 +17,7 @@ · 16 unmodified lines | |||
| 17 | 17 | ||
| 18 | #include <linux/clk.h> | 18 | #include <linux/clk.h> |
| 19 | #include <linux/delay.h> | 19 | #include <linux/delay.h> |
| 20 | #include <linux/extcon.h> | ||
| 20 | #include <linux/gpio/consumer.h> | 21 | #include <linux/gpio/consumer.h> |
| 21 | #include <linux/i2c.h> | 22 | #include <linux/i2c.h> |
| 22 | #include <linux/interrupt.h> | 23 | #include <linux/interrupt.h> |
| @@ -25,6 +26,7 @@ · 2 unmodified lines | |||
| 25 | #include <linux/list.h> | 26 | #include <linux/list.h> |
| 26 | #include <linux/module.h> | 27 | #include <linux/module.h> |
| 27 | #include <linux/mutex.h> | 28 | #include <linux/mutex.h> |
| 29 | #include <linux/of_graph.h> | ||
| 28 | #include <linux/regulator/consumer.h> | 30 | #include <linux/regulator/consumer.h> |
| 29 | #include <linux/slab.h> | 31 | #include <linux/slab.h> |
| 30 | 32 | ||
| @@ -81,6 +83,10 @@ struct sii8620 { · 50 unmodified lines | |||
| 81 | struct edid *edid; | 83 | struct edid *edid; |
| 82 | unsigned int gen2_write_burst:1; | 84 | unsigned int gen2_write_burst:1; |
| 83 | enum sii8620_mt_state mt_state; | 85 | enum sii8620_mt_state mt_state; |
| 86 | struct extcon_dev *extcon; | ||
| 87 | struct notifier_block extcon_nb; | ||
| 88 | struct work_struct extcon_wq; | ||
| 89 | int cable_state; | ||
| 84 | struct list_head mt_queue; | 90 | struct list_head mt_queue; |
| 85 | struct { | 91 | struct { |
| 86 | int r_size; | 92 | int r_size; |
| @@ -2170,6 +2176,77 @@ static void sii8620_init_rcp_input_dev(struct sii8620 *ctx) · 2083 unmodified lines | |||
| 2170 | ctx->rc_dev = rc_dev; | 2176 | ctx->rc_dev = rc_dev; |
| 2171 | } | 2177 | } |
| 2172 | 2178 | ||
| 2179 | static void sii8620_cable_out(struct sii8620 *ctx) | ||
| 2180 | { | ||
| 2181 | disable_irq(to_i2c_client(ctx->dev)->irq); | ||
| 2182 | sii8620_hw_off(ctx); | ||
| 2183 | } | ||
| 2184 | |||
| 2185 | static void sii8620_extcon_work(struct work_struct *work) | ||
| 2186 | { | ||
| 2187 | struct sii8620 *ctx = | ||
| 2188 | container_of(work, struct sii8620, extcon_wq); | ||
| 2189 | int state = extcon_get_state(ctx->extcon, EXTCON_DISP_MHL); | ||
| 2190 | |||
| 2191 | if (state == ctx->cable_state) | ||
| 2192 | return; | ||
| 2193 | |||
| 2194 | ctx->cable_state = state; | ||
| 2195 | |||
| 2196 | if (state > 0) | ||
| 2197 | sii8620_cable_in(ctx); | ||
| 2198 | else | ||
| 2199 | sii8620_cable_out(ctx); | ||
| 2200 | } | ||
| 2201 | |||
| 2202 | static int sii8620_extcon_notifier(struct notifier_block *self, | ||
| 2203 | unsigned long event, void *ptr) | ||
| 2204 | { | ||
| 2205 | struct sii8620 *ctx = | ||
| 2206 | container_of(self, struct sii8620, extcon_nb); | ||
| 2207 | |||
| 2208 | schedule_work(&ctx->extcon_wq); | ||
| 2209 | |||
| 2210 | return NOTIFY_DONE; | ||
| 2211 | } | ||
| 2212 | |||
| 2213 | static int sii8620_extcon_init(struct sii8620 *ctx) | ||
| 2214 | { | ||
| 2215 | struct extcon_dev *edev; | ||
| 2216 | struct device_node *musb, *muic; | ||
| 2217 | int ret; | ||
| 2218 | |||
| 2219 | /* get micro-USB connector node */ | ||
| 2220 | musb = of_graph_get_remote_node(ctx->dev->of_node, 1, -1); | ||
| 2221 | /* next get micro-USB Interface Controller node */ | ||
| 2222 | muic = of_get_next_parent(musb); | ||
| 2223 | |||
| 2224 | if (!muic) { | ||
| 2225 | dev_info(ctx->dev, "no extcon found, switching to 'always on' mode\n"); | ||
| 2226 | return 0; | ||
| 2227 | } | ||
| 2228 | |||
| 2229 | edev = extcon_find_edev_by_node(muic); | ||
| 2230 | of_node_put(muic); | ||
| 2231 | if (IS_ERR(edev)) { | ||
| 2232 | if (PTR_ERR(edev) == -EPROBE_DEFER) | ||
| 2233 | return -EPROBE_DEFER; | ||
| 2234 | dev_err(ctx->dev, "Invalid or missing extcon\n"); | ||
| 2235 | return PTR_ERR(edev); | ||
| 2236 | } | ||
| 2237 | |||
| 2238 | ctx->extcon = edev; | ||
| 2239 | ctx->extcon_nb.notifier_call = sii8620_extcon_notifier; | ||
| 2240 | INIT_WORK(&ctx->extcon_wq, sii8620_extcon_work); | ||
| 2241 | ret = extcon_register_notifier(edev, EXTCON_DISP_MHL, &ctx->extcon_nb); | ||
| 2242 | if (ret) { | ||
| 2243 | dev_err(ctx->dev, "failed to register notifier for MHL\n"); | ||
| 2244 | return ret; | ||
| 2245 | } | ||
| 2246 | |||
| 2247 | return 0; | ||
| 2248 | } | ||
| 2249 | |||
| 2173 | static inline struct sii8620 *bridge_to_sii8620(struct drm_bridge *bridge) | 2250 | static inline struct sii8620 *bridge_to_sii8620(struct drm_bridge *bridge) |
| 2174 | { | 2251 | { |
| 2175 | return container_of(bridge, struct sii8620, bridge); | 2252 | return container_of(bridge, struct sii8620, bridge); |
| @@ -2302,13 +2379,20 @@ static int sii8620_probe(struct i2c_client *client, · 126 unmodified lines | |||
| 2302 | if (ret) | 2379 | if (ret) |
| 2303 | return ret; | 2380 | return ret; |
| 2304 | 2381 | ||
| 2382 | ret = sii8620_extcon_init(ctx); | ||
| 2383 | if (ret < 0) { | ||
| 2384 | dev_err(ctx->dev, "failed to initialize EXTCON\n"); | ||
| 2385 | return ret; | ||
| 2386 | } | ||
| 2387 | |||
| 2305 | i2c_set_clientdata(client, ctx); | 2388 | i2c_set_clientdata(client, ctx); |
| 2306 | 2389 | ||
| 2307 | ctx->bridge.funcs = &sii8620_bridge_funcs; | 2390 | ctx->bridge.funcs = &sii8620_bridge_funcs; |
| 2308 | ctx->bridge.of_node = dev->of_node; | 2391 | ctx->bridge.of_node = dev->of_node; |
| 2309 | drm_bridge_add(&ctx->bridge); | 2392 | drm_bridge_add(&ctx->bridge); |
| 2310 | 2393 | ||
| 2311 | sii8620_cable_in(ctx); | 2394 | if (!ctx->extcon) |
| 2395 | sii8620_cable_in(ctx); | ||
| 2312 | 2396 | ||
| 2313 | return 0; | 2397 | return 0; |
| 2314 | } | 2398 | } |
| @@ -2317,8 +2401,15 @@ static int sii8620_remove(struct i2c_client *client) · 2 unmodified lines | |||
| 2317 | { | 2401 | { |
| 2318 | struct sii8620 *ctx = i2c_get_clientdata(client); | 2402 | struct sii8620 *ctx = i2c_get_clientdata(client); |
| 2319 | 2403 | ||
| 2320 | disable_irq(to_i2c_client(ctx->dev)->irq); | 2404 | if (ctx->extcon) { |
| 2321 | sii8620_hw_off(ctx); | 2405 | extcon_unregister_notifier(ctx->extcon, EXTCON_DISP_MHL, |
| 2406 | &ctx->extcon_nb); | ||
| 2407 | flush_work(&ctx->extcon_wq); | ||
| 2408 | if (ctx->cable_state > 0) | ||
| 2409 | sii8620_cable_out(ctx); | ||
| 2410 | } else { | ||
| 2411 | sii8620_cable_out(ctx); | ||
| 2412 | } | ||
| 2322 | drm_bridge_remove(&ctx->bridge); | 2413 | drm_bridge_remove(&ctx->bridge); |
| 2323 | 2414 | ||
| 2324 | return 0; | 2415 | return 0; |
| @@ -1,3 +1,5 @@ | |||
| 1 | # SPDX-License-Identifier: GPL-2.0 | ||
| 2 | |||
| 1 | menu "Microsoft Hyper-V guest support" | 3 | menu "Microsoft Hyper-V guest support" |
| 2 | 4 | ||
| 3 | config HYPERV | 5 | config HYPERV |
| @@ -4,6 +4,7 @@ obj-$(CONFIG_HYPERV_UTILS) += hv_utils.o · 3 unmodified lines | |||
| 4 | obj-$(CONFIG_HYPERV_BALLOON) += hv_balloon.o | 4 | obj-$(CONFIG_HYPERV_BALLOON) += hv_balloon.o |
| 5 | 5 | ||
| 6 | CFLAGS_hv_trace.o = -I$(src) | 6 | CFLAGS_hv_trace.o = -I$(src) |
| 7 | CFLAGS_hv_balloon.o = -I$(src) | ||
| 7 | 8 | ||
| 8 | hv_vmbus-y := vmbus_drv.o \ | 9 | hv_vmbus-y := vmbus_drv.o \ |
| 9 | hv.o connection.o channel.o \ | 10 | hv.o connection.o channel.o \ |
| @@ -71,7 +71,7 @@ static const struct vmbus_device vmbus_devs[] = { · 70 unmodified lines | |||
| 71 | /* PCIE */ | 71 | /* PCIE */ |
| 72 | { .dev_type = HV_PCIE, | 72 | { .dev_type = HV_PCIE, |
| 73 | HV_PCIE_GUID, | 73 | HV_PCIE_GUID, |
| 74 | .perf_device = true, | 74 | .perf_device = false, |
| 75 | }, | 75 | }, |
| 76 | 76 | ||
| 77 | /* Synthetic Frame Buffer */ | 77 | /* Synthetic Frame Buffer */ |
| @@ -596,10 +596,8 @@ static int next_numa_node_id; · 518 unmodified lines | |||
| 596 | /* | 596 | /* |
| 597 | * Starting with Win8, we can statically distribute the incoming | 597 | * Starting with Win8, we can statically distribute the incoming |
| 598 | * channel interrupt load by binding a channel to VCPU. | 598 | * channel interrupt load by binding a channel to VCPU. |
| 599 | * We do this in a hierarchical fashion: | 599 | * We distribute the interrupt loads to one or more NUMA nodes based on |
| 600 | * First distribute the primary channels across available NUMA nodes | 600 | * the channel's affinity_policy. |
| 601 | * and then distribute the subchannels amongst the CPUs in the NUMA | ||
| 602 | * node assigned to the primary channel. | ||
| 603 | * | 601 | * |
| 604 | * For pre-win8 hosts or non-performance critical channels we assign the | 602 | * For pre-win8 hosts or non-performance critical channels we assign the |
| 605 | * first CPU in the first NUMA node. | 603 | * first CPU in the first NUMA node. |
| @@ -27,7 +27,7 @@ · 26 unmodified lines | |||
| 27 | #include <linux/vmalloc.h> | 27 | #include <linux/vmalloc.h> |
| 28 | #include <linux/hyperv.h> | 28 | #include <linux/hyperv.h> |
| 29 | #include <linux/version.h> | 29 | #include <linux/version.h> |
| 30 | #include <linux/interrupt.h> | 30 | #include <linux/random.h> |
| 31 | #include <linux/clockchips.h> | 31 | #include <linux/clockchips.h> |
| 32 | #include <asm/hyperv.h> | 32 | #include <asm/hyperv.h> |
| 33 | #include <asm/mshyperv.h> | 33 | #include <asm/mshyperv.h> |
| @@ -38,6 +38,17 @@ struct hv_context hv_context = { · 4 unmodified lines | |||
| 38 | .synic_initialized = false, | 38 | .synic_initialized = false, |
| 39 | }; | 39 | }; |
| 40 | 40 | ||
| 41 | /* | ||
| 42 | * If false, we're using the old mechanism for stimer0 interrupts | ||
| 43 | * where it sends a VMbus message when it expires. The old | ||
| 44 | * mechanism is used when running on older versions of Hyper-V | ||
| 45 | * that don't support Direct Mode. While Hyper-V provides | ||
| 46 | * four stimer's per CPU, Linux uses only stimer0. | ||
| 47 | */ | ||
| 48 | static bool direct_mode_enabled; | ||
| 49 | static int stimer0_irq; | ||
| 50 | static int stimer0_vector; | ||
| 51 | |||
| 41 | #define HV_TIMER_FREQUENCY (10 * 1000 * 1000) /* 100ns period */ | 52 | #define HV_TIMER_FREQUENCY (10 * 1000 * 1000) /* 100ns period */ |
| 42 | #define HV_MAX_MAX_DELTA_TICKS 0xffffffff | 53 | #define HV_MAX_MAX_DELTA_TICKS 0xffffffff |
| 43 | #define HV_MIN_DELTA_TICKS 1 | 54 | #define HV_MIN_DELTA_TICKS 1 |
| @@ -53,6 +64,8 @@ int hv_init(void) · 9 unmodified lines | |||
| 53 | if (!hv_context.cpu_context) | 64 | if (!hv_context.cpu_context) |
| 54 | return -ENOMEM; | 65 | return -ENOMEM; |
| 55 | 66 | ||
| 67 | direct_mode_enabled = ms_hyperv.misc_features & | ||
| 68 | HV_X64_STIMER_DIRECT_MODE_AVAILABLE; | ||
| 56 | return 0; | 69 | return 0; |
| 57 | } | 70 | } |
| 58 | 71 | ||
| @@ -91,6 +104,21 @@ int hv_post_message(union hv_connection_id connection_id, · 32 unmodified lines | |||
| 91 | return status & 0xFFFF; | 104 | return status & 0xFFFF; |
| 92 | } | 105 | } |
| 93 | 106 | ||
| 107 | /* | ||
| 108 | * ISR for when stimer0 is operating in Direct Mode. Direct Mode | ||
| 109 | * does not use VMbus or any VMbus messages, so process here and not | ||
| 110 | * in the VMbus driver code. | ||
| 111 | */ | ||
| 112 | |||
| 113 | static void hv_stimer0_isr(void) | ||
| 114 | { | ||
| 115 | struct hv_per_cpu_context *hv_cpu; | ||
| 116 | |||
| 117 | hv_cpu = this_cpu_ptr(hv_context.cpu_context); | ||
| 118 | hv_cpu->clk_evt->event_handler(hv_cpu->clk_evt); | ||
| 119 | add_interrupt_randomness(stimer0_vector, 0); | ||
| 120 | } | ||
| 121 | |||
| 94 | static int hv_ce_set_next_event(unsigned long delta, | 122 | static int hv_ce_set_next_event(unsigned long delta, |
| 95 | struct clock_event_device *evt) | 123 | struct clock_event_device *evt) |
| 96 | { | 124 | { |
| @@ -108,6 +136,8 @@ static int hv_ce_shutdown(struct clock_event_device *evt) · 11 unmodified lines | |||
| 108 | { | 136 | { |
| 109 | hv_init_timer(HV_X64_MSR_STIMER0_COUNT, 0); | 137 | hv_init_timer(HV_X64_MSR_STIMER0_COUNT, 0); |
| 110 | hv_init_timer_config(HV_X64_MSR_STIMER0_CONFIG, 0); | 138 | hv_init_timer_config(HV_X64_MSR_STIMER0_CONFIG, 0); |
| 139 | if (direct_mode_enabled) | ||
| 140 | hv_disable_stimer0_percpu_irq(stimer0_irq); | ||
| 111 | 141 | ||
| 112 | return 0; | 142 | return 0; |
| 113 | } | 143 | } |
| @@ -116,11 +146,26 @@ static int hv_ce_set_oneshot(struct clock_event_device *evt) · 2 unmodified lines | |||
| 116 | { | 146 | { |
| 117 | union hv_timer_config timer_cfg; | 147 | union hv_timer_config timer_cfg; |
| 118 | 148 | ||
| 149 | timer_cfg.as_uint64 = 0; | ||
| 119 | timer_cfg.enable = 1; | 150 | timer_cfg.enable = 1; |
| 120 | timer_cfg.auto_enable = 1; | 151 | timer_cfg.auto_enable = 1; |
| 121 | timer_cfg.sintx = VMBUS_MESSAGE_SINT; | 152 | if (direct_mode_enabled) { |
| 153 | /* | ||
| 154 | * When it expires, the timer will directly interrupt | ||
| 155 | * on the specified hardware vector/IRQ. | ||
| 156 | */ | ||
| 157 | timer_cfg.direct_mode = 1; | ||
| 158 | timer_cfg.apic_vector = stimer0_vector; | ||
| 159 | hv_enable_stimer0_percpu_irq(stimer0_irq); | ||
| 160 | } else { | ||
| 161 | /* | ||
| 162 | * When it expires, the timer will generate a VMbus message, | ||
| 163 | * to be handled by the normal VMbus interrupt handler. | ||
| 164 | */ | ||
| 165 | timer_cfg.direct_mode = 0; | ||
| 166 | timer_cfg.sintx = VMBUS_MESSAGE_SINT; | ||
| 167 | } | ||
| 122 | hv_init_timer_config(HV_X64_MSR_STIMER0_CONFIG, timer_cfg.as_uint64); | 168 | hv_init_timer_config(HV_X64_MSR_STIMER0_CONFIG, timer_cfg.as_uint64); |
| 123 | |||
| 124 | return 0; | 169 | return 0; |
| 125 | } | 170 | } |
| 126 | 171 | ||
| @@ -147,7 +192,7 @@ int hv_synic_alloc(void) · 20 unmodified lines | |||
| 147 | int cpu; | 192 | int cpu; |
| 148 | 193 | ||
| 149 | hv_context.hv_numa_map = kzalloc(sizeof(struct cpumask) * nr_node_ids, | 194 | hv_context.hv_numa_map = kzalloc(sizeof(struct cpumask) * nr_node_ids, |
| 150 | GFP_ATOMIC); | 195 | GFP_KERNEL); |
| 151 | if (hv_context.hv_numa_map == NULL) { | 196 | if (hv_context.hv_numa_map == NULL) { |
| 152 | pr_err("Unable to allocate NUMA map\n"); | 197 | pr_err("Unable to allocate NUMA map\n"); |
| 153 | goto err; | 198 | goto err; |
| @@ -191,6 +236,11 @@ int hv_synic_alloc(void) · 37 unmodified lines | |||
| 191 | INIT_LIST_HEAD(&hv_cpu->chan_list); | 236 | INIT_LIST_HEAD(&hv_cpu->chan_list); |
| 192 | } | 237 | } |
| 193 | 238 | ||
| 239 | if (direct_mode_enabled && | ||
| 240 | hv_setup_stimer0_irq(&stimer0_irq, &stimer0_vector, | ||
| 241 | hv_stimer0_isr)) | ||
| 242 | goto err; | ||
| 243 | |||
| 194 | return 0; | 244 | return 0; |
| 195 | err: | 245 | err: |
| 196 | return -ENOMEM; | 246 | return -ENOMEM; |
| @@ -217,7 +267,7 @@ void hv_synic_free(void) · 20 unmodified lines | |||
| 217 | } | 267 | } |
| 218 | 268 | ||
| 219 | /* | 269 | /* |
| 220 | * hv_synic_init - Initialize the Synthethic Interrupt Controller. | 270 | * hv_synic_init - Initialize the Synthetic Interrupt Controller. |
| 221 | * | 271 | * |
| 222 | * If it is already initialized by another entity (ie x2v shim), we need to | 272 | * If it is already initialized by another entity (ie x2v shim), we need to |
| 223 | * retrieve the initialized message and event pages. Otherwise, we create and | 273 | * retrieve the initialized message and event pages. Otherwise, we create and |
| @@ -252,7 +302,6 @@ int hv_synic_init(unsigned int cpu) · 28 unmodified lines | |||
| 252 | hv_get_synint_state(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, | 302 | hv_get_synint_state(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, |
| 253 | shared_sint.as_uint64); | 303 | shared_sint.as_uint64); |
| 254 | 304 | ||
| 255 | shared_sint.as_uint64 = 0; | ||
| 256 | shared_sint.vector = HYPERVISOR_CALLBACK_VECTOR; | 305 | shared_sint.vector = HYPERVISOR_CALLBACK_VECTOR; |
| 257 | shared_sint.masked = false; | 306 | shared_sint.masked = false; |
| 258 | if (ms_hyperv.hints & HV_X64_DEPRECATING_AEOI_RECOMMENDED) | 307 | if (ms_hyperv.hints & HV_X64_DEPRECATING_AEOI_RECOMMENDED) |
| @@ -292,6 +341,9 @@ void hv_synic_clockevents_cleanup(void) · 33 unmodified lines | |||
| 292 | if (!(ms_hyperv.features & HV_X64_MSR_SYNTIMER_AVAILABLE)) | 341 | if (!(ms_hyperv.features & HV_X64_MSR_SYNTIMER_AVAILABLE)) |
| 293 | return; | 342 | return; |
| 294 | 343 | ||
| 344 | if (direct_mode_enabled) | ||
| 345 | hv_remove_stimer0_irq(stimer0_irq); | ||
| 346 | |||
| 295 | for_each_present_cpu(cpu) { | 347 | for_each_present_cpu(cpu) { |
| 296 | struct hv_per_cpu_context *hv_cpu | 348 | struct hv_per_cpu_context *hv_cpu |
| 297 | = per_cpu_ptr(hv_context.cpu_context, cpu); | 349 | = per_cpu_ptr(hv_context.cpu_context, cpu); |
| @@ -34,6 +34,9 @@ · 33 unmodified lines | |||
| 34 | 34 | ||
| 35 | #include <linux/hyperv.h> | 35 | #include <linux/hyperv.h> |
| 36 | 36 | ||
| 37 | #define CREATE_TRACE_POINTS | ||
| 38 | #include "hv_trace_balloon.h" | ||
| 39 | |||
| 37 | /* | 40 | /* |
| 38 | * We begin with definitions supporting the Dynamic Memory protocol | 41 | * We begin with definitions supporting the Dynamic Memory protocol |
| 39 | * with the host. | 42 | * with the host. |
| @@ -576,11 +579,65 @@ static struct hv_dynmem_device dm_device; · 536 unmodified lines | |||
| 576 | static void post_status(struct hv_dynmem_device *dm); | 579 | static void post_status(struct hv_dynmem_device *dm); |
| 577 | 580 | ||
| 578 | #ifdef CONFIG_MEMORY_HOTPLUG | 581 | #ifdef CONFIG_MEMORY_HOTPLUG |
| 582 | static inline bool has_pfn_is_backed(struct hv_hotadd_state *has, | ||
| 583 | unsigned long pfn) | ||
| 584 | { | ||
| 585 | struct hv_hotadd_gap *gap; | ||
| 586 | |||
| 587 | /* The page is not backed. */ | ||
| 588 | if ((pfn < has->covered_start_pfn) || (pfn >= has->covered_end_pfn)) | ||
| 589 | return false; | ||
| 590 | |||
| 591 | /* Check for gaps. */ | ||
| 592 | list_for_each_entry(gap, &has->gap_list, list) { | ||
| 593 | if ((pfn >= gap->start_pfn) && (pfn < gap->end_pfn)) | ||
| 594 | return false; | ||
| 595 | } | ||
| 596 | |||
| 597 | return true; | ||
| 598 | } | ||
| 599 | |||
| 600 | static unsigned long hv_page_offline_check(unsigned long start_pfn, | ||
| 601 | unsigned long nr_pages) | ||
| 602 | { | ||
| 603 | unsigned long pfn = start_pfn, count = 0; | ||
| 604 | struct hv_hotadd_state *has; | ||
| 605 | bool found; | ||
| 606 | |||
| 607 | while (pfn < start_pfn + nr_pages) { | ||
| 608 | /* | ||
| 609 | * Search for HAS which covers the pfn and when we find one | ||
| 610 | * count how many consequitive PFNs are covered. | ||
| 611 | */ | ||
| 612 | found = false; | ||
| 613 | list_for_each_entry(has, &dm_device.ha_region_list, list) { | ||
| 614 | while ((pfn >= has->start_pfn) && | ||
| 615 | (pfn < has->end_pfn) && | ||
| 616 | (pfn < start_pfn + nr_pages)) { | ||
| 617 | found = true; | ||
| 618 | if (has_pfn_is_backed(has, pfn)) | ||
| 619 | count++; | ||
| 620 | pfn++; | ||
| 621 | } | ||
| 622 | } | ||
| 623 | |||
| 624 | /* | ||
| 625 | * This PFN is not in any HAS (e.g. we're offlining a region | ||
| 626 | * which was present at boot), no need to account for it. Go | ||
| 627 | * to the next one. | ||
| 628 | */ | ||
| 629 | if (!found) | ||
| 630 | pfn++; | ||
| 631 | } | ||
| 632 | |||
| 633 | return count; | ||
| 634 | } | ||
| 635 | |||
| 579 | static int hv_memory_notifier(struct notifier_block *nb, unsigned long val, | 636 | static int hv_memory_notifier(struct notifier_block *nb, unsigned long val, |
| 580 | void *v) | 637 | void *v) |
| 581 | { | 638 | { |
| 582 | struct memory_notify *mem = (struct memory_notify *)v; | 639 | struct memory_notify *mem = (struct memory_notify *)v; |
| 583 | unsigned long flags; | 640 | unsigned long flags, pfn_count; |
| 584 | 641 | ||
| 585 | switch (val) { | 642 | switch (val) { |
| 586 | case MEM_ONLINE: | 643 | case MEM_ONLINE: |
| @@ -593,7 +650,19 @@ static int hv_memory_notifier(struct notifier_block *nb, unsigned long val, · 6 unmodified lines | |||
| 593 | 650 | ||
| 594 | case MEM_OFFLINE: | 651 | case MEM_OFFLINE: |
| 595 | spin_lock_irqsave(&dm_device.ha_lock, flags); | 652 | spin_lock_irqsave(&dm_device.ha_lock, flags); |
| 596 | dm_device.num_pages_onlined -= mem->nr_pages; | 653 | pfn_count = hv_page_offline_check(mem->start_pfn, |
| 654 | mem->nr_pages); | ||
| 655 | if (pfn_count <= dm_device.num_pages_onlined) { | ||
| 656 | dm_device.num_pages_onlined -= pfn_count; | ||
| 657 | } else { | ||
| 658 | /* | ||
| 659 | * We're offlining more pages than we managed to online. | ||
| 660 | * This is unexpected. In any case don't let | ||
| 661 | * num_pages_onlined wrap around zero. | ||
| 662 | */ | ||
| 663 | WARN_ON_ONCE(1); | ||
| 664 | dm_device.num_pages_onlined = 0; | ||
| 665 | } | ||
| 597 | spin_unlock_irqrestore(&dm_device.ha_lock, flags); | 666 | spin_unlock_irqrestore(&dm_device.ha_lock, flags); |
| 598 | break; | 667 | break; |
| 599 | case MEM_GOING_ONLINE: | 668 | case MEM_GOING_ONLINE: |
| @@ -612,30 +681,9 @@ static struct notifier_block hv_memory_nb = { · 12 unmodified lines | |||
| 612 | /* Check if the particular page is backed and can be onlined and online it. */ | 681 | /* Check if the particular page is backed and can be onlined and online it. */ |
| 613 | static void hv_page_online_one(struct hv_hotadd_state *has, struct page *pg) | 682 | static void hv_page_online_one(struct hv_hotadd_state *has, struct page *pg) |
| 614 | { | 683 | { |
| 615 | unsigned long cur_start_pgp; | 684 | if (!has_pfn_is_backed(has, page_to_pfn(pg))) |
| 616 | unsigned long cur_end_pgp; | ||
| 617 | struct hv_hotadd_gap *gap; | ||
| 618 | |||
| 619 | cur_start_pgp = (unsigned long)pfn_to_page(has->covered_start_pfn); | ||
| 620 | cur_end_pgp = (unsigned long)pfn_to_page(has->covered_end_pfn); | ||
| 621 | |||
| 622 | /* The page is not backed. */ | ||
| 623 | if (((unsigned long)pg < cur_start_pgp) || | ||
| 624 | ((unsigned long)pg >= cur_end_pgp)) | ||
| 625 | return; | 685 | return; |
| 626 | 686 | ||
| 627 | /* Check for gaps. */ | ||
| 628 | list_for_each_entry(gap, &has->gap_list, list) { | ||
| 629 | cur_start_pgp = (unsigned long) | ||
| 630 | pfn_to_page(gap->start_pfn); | ||
| 631 | cur_end_pgp = (unsigned long) | ||
| 632 | pfn_to_page(gap->end_pfn); | ||
| 633 | if (((unsigned long)pg >= cur_start_pgp) && | ||
| 634 | ((unsigned long)pg < cur_end_pgp)) { | ||
| 635 | return; | ||
| 636 | } | ||
| 637 | } | ||
| 638 | |||
| 639 | /* This frame is currently backed; online the page. */ | 687 | /* This frame is currently backed; online the page. */ |
| 640 | __online_page_set_limits(pg); | 688 | __online_page_set_limits(pg); |
| 641 | __online_page_increment_counters(pg); | 689 | __online_page_increment_counters(pg); |
| @@ -691,7 +739,7 @@ static void hv_mem_hot_add(unsigned long start, unsigned long size, · 49 unmodified lines | |||
| 691 | (HA_CHUNK << PAGE_SHIFT)); | 739 | (HA_CHUNK << PAGE_SHIFT)); |
| 692 | 740 | ||
| 693 | if (ret) { | 741 | if (ret) { |
| 694 | pr_warn("hot_add memory failed error is %d\n", ret); | 742 | pr_err("hot_add memory failed error is %d\n", ret); |
| 695 | if (ret == -EEXIST) { | 743 | if (ret == -EEXIST) { |
| 696 | /* | 744 | /* |
| 697 | * This error indicates that the error | 745 | * This error indicates that the error |
| @@ -726,19 +774,13 @@ static void hv_mem_hot_add(unsigned long start, unsigned long size, · 28 unmodified lines | |||
| 726 | static void hv_online_page(struct page *pg) | 774 | static void hv_online_page(struct page *pg) |
| 727 | { | 775 | { |
| 728 | struct hv_hotadd_state *has; | 776 | struct hv_hotadd_state *has; |
| 729 | unsigned long cur_start_pgp; | ||
| 730 | unsigned long cur_end_pgp; | ||
| 731 | unsigned long flags; | 777 | unsigned long flags; |
| 778 | unsigned long pfn = page_to_pfn(pg); | ||
| 732 | 779 | ||
| 733 | spin_lock_irqsave(&dm_device.ha_lock, flags); | 780 | spin_lock_irqsave(&dm_device.ha_lock, flags); |
| 734 | list_for_each_entry(has, &dm_device.ha_region_list, list) { | 781 | list_for_each_entry(has, &dm_device.ha_region_list, list) { |
| 735 | cur_start_pgp = (unsigned long) | ||
| 736 | pfn_to_page(has->start_pfn); | ||
| 737 | cur_end_pgp = (unsigned long)pfn_to_page(has->end_pfn); | ||
| 738 | |||
| 739 | /* The page belongs to a different HAS. */ | 782 | /* The page belongs to a different HAS. */ |
| 740 | if (((unsigned long)pg < cur_start_pgp) || | 783 | if ((pfn < has->start_pfn) || (pfn >= has->end_pfn)) |
| 741 | ((unsigned long)pg >= cur_end_pgp)) | ||
| 742 | continue; | 784 | continue; |
| 743 | 785 | ||
| 744 | hv_page_online_one(has, pg); | 786 | hv_page_online_one(has, pg); |
| @@ -1014,7 +1056,7 @@ static void hot_add_req(struct work_struct *dummy) · 269 unmodified lines | |||
| 1014 | resp.result = 0; | 1056 | resp.result = 0; |
| 1015 | 1057 | ||
| 1016 | if (!do_hot_add || (resp.page_count == 0)) | 1058 | if (!do_hot_add || (resp.page_count == 0)) |
| 1017 | pr_info("Memory hot add failed\n"); | 1059 | pr_err("Memory hot add failed\n"); |
| 1018 | 1060 | ||
| 1019 | dm->state = DM_INITIALIZED; | 1061 | dm->state = DM_INITIALIZED; |
| 1020 | resp.hdr.trans_id = atomic_inc_return(&trans_id); | 1062 | resp.hdr.trans_id = atomic_inc_return(&trans_id); |
| @@ -1041,7 +1083,7 @@ static void process_info(struct hv_dynmem_device *dm, struct dm_info_msg *msg) · 20 unmodified lines | |||
| 1041 | 1083 | ||
| 1042 | break; | 1084 | break; |
| 1043 | default: | 1085 | default: |
| 1044 | pr_info("Received Unknown type: %d\n", info_hdr->type); | 1086 | pr_warn("Received Unknown type: %d\n", info_hdr->type); |
| 1045 | } | 1087 | } |
| 1046 | } | 1088 | } |
| 1047 | 1089 | ||
| @@ -1120,6 +1162,9 @@ static void post_status(struct hv_dynmem_device *dm) · 72 unmodified lines | |||
| 1120 | dm->num_pages_added - dm->num_pages_onlined : 0) + | 1162 | dm->num_pages_added - dm->num_pages_onlined : 0) + |
| 1121 | compute_balloon_floor(); | 1163 | compute_balloon_floor(); |
| 1122 | 1164 | ||
| 1165 | trace_balloon_status(status.num_avail, status.num_committed, | ||
| 1166 | vm_memory_committed(), dm->num_pages_ballooned, | ||
| 1167 | dm->num_pages_added, dm->num_pages_onlined); | ||
| 1123 | /* | 1168 | /* |
| 1124 | * If our transaction ID is no longer current, just don't | 1169 | * If our transaction ID is no longer current, just don't |
| 1125 | * send the status. This can happen if we were interrupted | 1170 | * send the status. This can happen if we were interrupted |
| @@ -1290,7 +1335,7 @@ static void balloon_up(struct work_struct *dummy) · 164 unmodified lines | |||
| 1290 | /* | 1335 | /* |
| 1291 | * Free up the memory we allocatted. | 1336 | * Free up the memory we allocatted. |
| 1292 | */ | 1337 | */ |
| 1293 | pr_info("Balloon response failed\n"); | 1338 | pr_err("Balloon response failed\n"); |
| 1294 | 1339 | ||
| 1295 | for (i = 0; i < bl_resp->range_count; i++) | 1340 | for (i = 0; i < bl_resp->range_count; i++) |
| 1296 | free_balloon_pages(&dm_device, | 1341 | free_balloon_pages(&dm_device, |
| @@ -1421,7 +1466,7 @@ static void cap_resp(struct hv_dynmem_device *dm, · 124 unmodified lines | |||
| 1421 | struct dm_capabilities_resp_msg *cap_resp) | 1466 | struct dm_capabilities_resp_msg *cap_resp) |
| 1422 | { | 1467 | { |
| 1423 | if (!cap_resp->is_accepted) { | 1468 | if (!cap_resp->is_accepted) { |
| 1424 | pr_info("Capabilities not accepted by host\n"); | 1469 | pr_err("Capabilities not accepted by host\n"); |
| 1425 | dm->state = DM_INIT_ERROR; | 1470 | dm->state = DM_INIT_ERROR; |
| 1426 | } | 1471 | } |
| 1427 | complete(&dm->host_event); | 1472 | complete(&dm->host_event); |
| @@ -1508,7 +1553,7 @@ static void balloon_onchannelcallback(void *context) · 80 unmodified lines | |||
| 1508 | break; | 1553 | break; |
| 1509 | 1554 | ||
| 1510 | default: | 1555 | default: |
| 1511 | pr_err("Unhandled message: type: %d\n", dm_hdr->type); | 1556 | pr_warn("Unhandled message: type: %d\n", dm_hdr->type); |
| 1512 | 1557 | ||
| 1513 | } | 1558 | } |
| 1514 | } | 1559 | } |
| @@ -1,3 +1,5 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 2 | |||
| 1 | #include "hyperv_vmbus.h" | 3 | #include "hyperv_vmbus.h" |
| 2 | 4 | ||
| 3 | #define CREATE_TRACE_POINTS | 5 | #define CREATE_TRACE_POINTS |
| @@ -1,3 +1,5 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 2 | |||
| 1 | #undef TRACE_SYSTEM | 3 | #undef TRACE_SYSTEM |
| 2 | #define TRACE_SYSTEM hyperv | 4 | #define TRACE_SYSTEM hyperv |
| 3 | 5 | ||
| @@ -0,0 +1,48 @@ | |||
| 1 | #undef TRACE_SYSTEM | ||
| 2 | #define TRACE_SYSTEM hyperv | ||
| 3 | |||
| 4 | #if !defined(_HV_TRACE_BALLOON_H) || defined(TRACE_HEADER_MULTI_READ) | ||
| 5 | #define _HV_TRACE_BALLOON_H | ||
| 6 | |||
| 7 | #include <linux/tracepoint.h> | ||
| 8 | |||
| 9 | TRACE_EVENT(balloon_status, | ||
| 10 | TP_PROTO(u64 available, u64 committed, | ||
| 11 | unsigned long vm_memory_committed, | ||
| 12 | unsigned long pages_ballooned, | ||
| 13 | unsigned long pages_added, | ||
| 14 | unsigned long pages_onlined), | ||
| 15 | TP_ARGS(available, committed, vm_memory_committed, | ||
| 16 | pages_ballooned, pages_added, pages_onlined), | ||
| 17 | TP_STRUCT__entry( | ||
| 18 | __field(u64, available) | ||
| 19 | __field(u64, committed) | ||
| 20 | __field(unsigned long, vm_memory_committed) | ||
| 21 | __field(unsigned long, pages_ballooned) | ||
| 22 | __field(unsigned long, pages_added) | ||
| 23 | __field(unsigned long, pages_onlined) | ||
| 24 | ), | ||
| 25 | TP_fast_assign( | ||
| 26 | __entry->available = available; | ||
| 27 | __entry->committed = committed; | ||
| 28 | __entry->vm_memory_committed = vm_memory_committed; | ||
| 29 | __entry->pages_ballooned = pages_ballooned; | ||
| 30 | __entry->pages_added = pages_added; | ||
| 31 | __entry->pages_onlined = pages_onlined; | ||
| 32 | ), | ||
| 33 | TP_printk("available %lld, committed %lld; vm_memory_committed %ld;" | ||
| 34 | " pages_ballooned %ld, pages_added %ld, pages_onlined %ld", | ||
| 35 | __entry->available, __entry->committed, | ||
| 36 | __entry->vm_memory_committed, __entry->pages_ballooned, | ||
| 37 | __entry->pages_added, __entry->pages_onlined | ||
| 38 | ) | ||
| 39 | ); | ||
| 40 | |||
| 41 | #undef TRACE_INCLUDE_PATH | ||
| 42 | #define TRACE_INCLUDE_PATH . | ||
| 43 | #undef TRACE_INCLUDE_FILE | ||
| 44 | #define TRACE_INCLUDE_FILE hv_trace_balloon | ||
| 45 | #endif /* _HV_TRACE_BALLOON_H */ | ||
| 46 | |||
| 47 | /* This part must be outside protection */ | ||
| 48 | #include <trace/define_trace.h> | ||
| @@ -57,7 +57,9 @@ union hv_timer_config { · 56 unmodified lines | |||
| 57 | u64 periodic:1; | 57 | u64 periodic:1; |
| 58 | u64 lazy:1; | 58 | u64 lazy:1; |
| 59 | u64 auto_enable:1; | 59 | u64 auto_enable:1; |
| 60 | u64 reserved_z0:12; | 60 | u64 apic_vector:8; |
| 61 | u64 direct_mode:1; | ||
| 62 | u64 reserved_z0:3; | ||
| 61 | u64 sintx:4; | 63 | u64 sintx:4; |
| 62 | u64 reserved_z1:44; | 64 | u64 reserved_z1:44; |
| 63 | }; | 65 | }; |
| @@ -0,0 +1,7 @@ | |||
| 1 | menu "HW tracing support" | ||
| 2 | |||
| 3 | source "drivers/hwtracing/stm/Kconfig" | ||
| 4 | |||
| 5 | source "drivers/hwtracing/intel_th/Kconfig" | ||
| 6 | |||
| 7 | endmenu | ||
| @@ -315,7 +315,7 @@ static void debug_dump_regs(struct debug_drvdata *drvdata) · 314 unmodified lines | |||
| 315 | } | 315 | } |
| 316 | 316 | ||
| 317 | pc = debug_adjust_pc(drvdata); | 317 | pc = debug_adjust_pc(drvdata); |
| 318 | dev_emerg(dev, " EDPCSR: [<%p>] %pS\n", (void *)pc, (void *)pc); | 318 | dev_emerg(dev, " EDPCSR: [<%px>] %pS\n", (void *)pc, (void *)pc); |
| 319 | 319 | ||
| 320 | if (drvdata->edcidsr_present) | 320 | if (drvdata->edcidsr_present) |
| 321 | dev_emerg(dev, " EDCIDSR: %08x\n", drvdata->edcidsr); | 321 | dev_emerg(dev, " EDCIDSR: %08x\n", drvdata->edcidsr); |
| @@ -1780,7 +1780,7 @@ static ssize_t ctxid_masks_store(struct device *dev, · 1779 unmodified lines | |||
| 1780 | */ | 1780 | */ |
| 1781 | for (j = 0; j < 8; j++) { | 1781 | for (j = 0; j < 8; j++) { |
| 1782 | if (maskbyte & 1) | 1782 | if (maskbyte & 1) |
| 1783 | config->ctxid_pid[i] &= ~(0xFF << (j * 8)); | 1783 | config->ctxid_pid[i] &= ~(0xFFUL << (j * 8)); |
| 1784 | maskbyte >>= 1; | 1784 | maskbyte >>= 1; |
| 1785 | } | 1785 | } |
| 1786 | /* Select the next ctxid comparator mask value */ | 1786 | /* Select the next ctxid comparator mask value */ |
| @@ -1963,7 +1963,7 @@ static ssize_t vmid_masks_store(struct device *dev, · 176 unmodified lines | |||
| 1963 | */ | 1963 | */ |
| 1964 | for (j = 0; j < 8; j++) { | 1964 | for (j = 0; j < 8; j++) { |
| 1965 | if (maskbyte & 1) | 1965 | if (maskbyte & 1) |
| 1966 | config->vmid_val[i] &= ~(0xFF << (j * 8)); | 1966 | config->vmid_val[i] &= ~(0xFFUL << (j * 8)); |
| 1967 | maskbyte >>= 1; | 1967 | maskbyte >>= 1; |
| 1968 | } | 1968 | } |
| 1969 | /* Select the next vmid comparator mask value */ | 1969 | /* Select the next vmid comparator mask value */ |
| @@ -25,6 +25,18 @@ config INTEL_TH_PCI · 24 unmodified lines | |||
| 25 | 25 | ||
| 26 | Say Y here to enable PCI Intel TH support. | 26 | Say Y here to enable PCI Intel TH support. |
| 27 | 27 | ||
| 28 | config INTEL_TH_ACPI | ||
| 29 | tristate "Intel(R) Trace Hub ACPI controller" | ||
| 30 | depends on ACPI | ||
| 31 | help | ||
| 32 | Intel(R) Trace Hub may exist as an ACPI device. This option enables | ||
| 33 | support glue layer for ACPI-based Intel TH. This typically implies | ||
| 34 | 'host debugger' mode, that is, the trace configuration and capture | ||
| 35 | is handled by an external debug host and corresponding controls will | ||
| 36 | not be available on the target. | ||
| 37 | |||
| 38 | Say Y here to enable ACPI Intel TH support. | ||
| 39 | |||
| 28 | config INTEL_TH_GTH | 40 | config INTEL_TH_GTH |
| 29 | tristate "Intel(R) Trace Hub Global Trace Hub" | 41 | tristate "Intel(R) Trace Hub Global Trace Hub" |
| 30 | help | 42 | help |
| @@ -6,6 +6,9 @@ intel_th-$(CONFIG_INTEL_TH_DEBUG) += debug.o · 5 unmodified lines | |||
| 6 | obj-$(CONFIG_INTEL_TH_PCI) += intel_th_pci.o | 6 | obj-$(CONFIG_INTEL_TH_PCI) += intel_th_pci.o |
| 7 | intel_th_pci-y := pci.o | 7 | intel_th_pci-y := pci.o |
| 8 | 8 | ||
| 9 | obj-$(CONFIG_INTEL_TH_ACPI) += intel_th_acpi.o | ||
| 10 | intel_th_acpi-y := acpi.o | ||
| 11 | |||
| 9 | obj-$(CONFIG_INTEL_TH_GTH) += intel_th_gth.o | 12 | obj-$(CONFIG_INTEL_TH_GTH) += intel_th_gth.o |
| 10 | intel_th_gth-y := gth.o | 13 | intel_th_gth-y := gth.o |
| 11 | 14 | ||
| @@ -0,0 +1,79 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 2 | /* | ||
| 3 | * Intel(R) Trace Hub ACPI driver | ||
| 4 | * | ||
| 5 | * Copyright (C) 2017 Intel Corporation. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
| 9 | |||
| 10 | #include <linux/types.h> | ||
| 11 | #include <linux/module.h> | ||
| 12 | #include <linux/device.h> | ||
| 13 | #include <linux/sysfs.h> | ||
| 14 | #include <linux/platform_device.h> | ||
| 15 | #include <linux/acpi.h> | ||
| 16 | |||
| 17 | #include "intel_th.h" | ||
| 18 | |||
| 19 | #define DRIVER_NAME "intel_th_acpi" | ||
| 20 | |||
| 21 | static const struct intel_th_drvdata intel_th_acpi_pch = { | ||
| 22 | .host_mode_only = 1, | ||
| 23 | }; | ||
| 24 | |||
| 25 | static const struct intel_th_drvdata intel_th_acpi_uncore = { | ||
| 26 | .host_mode_only = 1, | ||
| 27 | }; | ||
| 28 | |||
| 29 | static const struct acpi_device_id intel_th_acpi_ids[] = { | ||
| 30 | { "INTC1000", (kernel_ulong_t)&intel_th_acpi_uncore }, | ||
| 31 | { "INTC1001", (kernel_ulong_t)&intel_th_acpi_pch }, | ||
| 32 | { "", 0 }, | ||
| 33 | }; | ||
| 34 | |||
| 35 | MODULE_DEVICE_TABLE(acpi, intel_th_acpi_ids); | ||
| 36 | |||
| 37 | static int intel_th_acpi_probe(struct platform_device *pdev) | ||
| 38 | { | ||
| 39 | struct acpi_device *adev = ACPI_COMPANION(&pdev->dev); | ||
| 40 | const struct acpi_device_id *id; | ||
| 41 | struct intel_th *th; | ||
| 42 | |||
| 43 | id = acpi_match_device(intel_th_acpi_ids, &pdev->dev); | ||
| 44 | if (!id) | ||
| 45 | return -ENODEV; | ||
| 46 | |||
| 47 | th = intel_th_alloc(&pdev->dev, (void *)id->driver_data, | ||
| 48 | pdev->resource, pdev->num_resources, -1); | ||
| 49 | if (IS_ERR(th)) | ||
| 50 | return PTR_ERR(th); | ||
| 51 | |||
| 52 | adev->driver_data = th; | ||
| 53 | |||
| 54 | return 0; | ||
| 55 | } | ||
| 56 | |||
| 57 | static int intel_th_acpi_remove(struct platform_device *pdev) | ||
| 58 | { | ||
| 59 | struct intel_th *th = platform_get_drvdata(pdev); | ||
| 60 | |||
| 61 | intel_th_free(th); | ||
| 62 | |||
| 63 | return 0; | ||
| 64 | } | ||
| 65 | |||
| 66 | static struct platform_driver intel_th_acpi_driver = { | ||
| 67 | .probe = intel_th_acpi_probe, | ||
| 68 | .remove = intel_th_acpi_remove, | ||
| 69 | .driver = { | ||
| 70 | .name = DRIVER_NAME, | ||
| 71 | .acpi_match_table = intel_th_acpi_ids, | ||
| 72 | }, | ||
| 73 | }; | ||
| 74 | |||
| 75 | module_platform_driver(intel_th_acpi_driver); | ||
| 76 | |||
| 77 | MODULE_LICENSE("GPL v2"); | ||
| 78 | MODULE_DESCRIPTION("Intel(R) Trace Hub ACPI controller driver"); | ||
| 79 | MODULE_AUTHOR("Alexander Shishkin <alexander.shishkin@intel.com>"); | ||
| @@ -1,16 +1,8 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 1 | /* | 2 | /* |
| 2 | * Intel(R) Trace Hub driver core | 3 | * Intel(R) Trace Hub driver core |
| 3 | * | 4 | * |
| 4 | * Copyright (C) 2014-2015 Intel Corporation. | 5 | * Copyright (C) 2014-2015 Intel 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 | */ | 6 | */ |
| 15 | 7 | ||
| 16 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | 8 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
| @@ -638,7 +630,8 @@ intel_th_subdevice_alloc(struct intel_th *th, · 621 unmodified lines | |||
| 638 | thdev->output.port = -1; | 630 | thdev->output.port = -1; |
| 639 | thdev->output.scratchpad = subdev->scrpd; | 631 | thdev->output.scratchpad = subdev->scrpd; |
| 640 | } else if (subdev->type == INTEL_TH_SWITCH) { | 632 | } else if (subdev->type == INTEL_TH_SWITCH) { |
| 641 | thdev->host_mode = host_mode; | 633 | thdev->host_mode = |
| 634 | INTEL_TH_CAP(th, host_mode_only) ? true : host_mode; | ||
| 642 | th->hub = thdev; | 635 | th->hub = thdev; |
| 643 | } | 636 | } |
| 644 | 637 | ||
| @@ -737,7 +730,8 @@ static int intel_th_populate(struct intel_th *th) · 92 unmodified lines | |||
| 737 | struct intel_th_device *thdev; | 730 | struct intel_th_device *thdev; |
| 738 | 731 | ||
| 739 | /* only allow SOURCE and SWITCH devices in host mode */ | 732 | /* only allow SOURCE and SWITCH devices in host mode */ |
| 740 | if (host_mode && subdev->type == INTEL_TH_OUTPUT) | 733 | if ((INTEL_TH_CAP(th, host_mode_only) || host_mode) && |
| 734 | subdev->type == INTEL_TH_OUTPUT) | ||
| 741 | continue; | 735 | continue; |
| 742 | 736 | ||
| 743 | /* | 737 | /* |
| @@ -813,7 +807,14 @@ intel_th_alloc(struct device *dev, struct intel_th_drvdata *drvdata, · 69 unmodified lines | |||
| 813 | struct resource *devres, unsigned int ndevres, int irq) | 807 | struct resource *devres, unsigned int ndevres, int irq) |
| 814 | { | 808 | { |
| 815 | struct intel_th *th; | 809 | struct intel_th *th; |
| 816 | int err; | 810 | int err, r; |
| 811 | |||
| 812 | if (irq == -1) | ||
| 813 | for (r = 0; r < ndevres; r++) | ||
| 814 | if (devres[r].flags & IORESOURCE_IRQ) { | ||
| 815 | irq = devres[r].start; | ||
| 816 | break; | ||
| 817 | } | ||
| 817 | 818 | ||
| 818 | th = kzalloc(sizeof(*th), GFP_KERNEL); | 819 | th = kzalloc(sizeof(*th), GFP_KERNEL); |
| 819 | if (!th) | 820 | if (!th) |
| @@ -935,9 +936,13 @@ EXPORT_SYMBOL_GPL(intel_th_trace_disable); · 115 unmodified lines | |||
| 935 | int intel_th_set_output(struct intel_th_device *thdev, | 936 | int intel_th_set_output(struct intel_th_device *thdev, |
| 936 | unsigned int master) | 937 | unsigned int master) |
| 937 | { | 938 | { |
| 938 | struct intel_th_device *hub = to_intel_th_device(thdev->dev.parent); | 939 | struct intel_th_device *hub = to_intel_th_hub(thdev); |
| 939 | struct intel_th_driver *hubdrv = to_intel_th_driver(hub->dev.driver); | 940 | struct intel_th_driver *hubdrv = to_intel_th_driver(hub->dev.driver); |
| 940 | 941 | ||
| 942 | /* In host mode, this is up to the external debugger, do nothing. */ | ||
| 943 | if (hub->host_mode) | ||
| 944 | return 0; | ||
| 945 | |||
| 941 | if (!hubdrv->set_output) | 946 | if (!hubdrv->set_output) |
| 942 | return -ENOTSUPP; | 947 | return -ENOTSUPP; |
| 943 | 948 | ||
| @@ -1,16 +1,8 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 1 | /* | 2 | /* |
| 2 | * Intel(R) Trace Hub driver debugging | 3 | * Intel(R) Trace Hub driver debugging |
| 3 | * | 4 | * |
| 4 | * Copyright (C) 2014-2015 Intel Corporation. | 5 | * Copyright (C) 2014-2015 Intel 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 | */ | 6 | */ |
| 15 | 7 | ||
| 16 | #include <linux/types.h> | 8 | #include <linux/types.h> |
| @@ -1,16 +1,8 @@ | |||
| 1 | /* SPDX-License-Identifier: GPL-2.0 */ | ||
| 1 | /* | 2 | /* |
| 2 | * Intel(R) Trace Hub driver debugging | 3 | * Intel(R) Trace Hub driver debugging |
| 3 | * | 4 | * |
| 4 | * Copyright (C) 2014-2015 Intel Corporation. | 5 | * Copyright (C) 2014-2015 Intel 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 | */ | 6 | */ |
| 15 | 7 | ||
| 16 | #ifndef __INTEL_TH_DEBUG_H__ | 8 | #ifndef __INTEL_TH_DEBUG_H__ |
| @@ -1,16 +1,8 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 1 | /* | 2 | /* |
| 2 | * Intel(R) Trace Hub Global Trace Hub | 3 | * Intel(R) Trace Hub Global Trace Hub |
| 3 | * | 4 | * |
| 4 | * Copyright (C) 2014-2015 Intel Corporation. | 5 | * Copyright (C) 2014-2015 Intel 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 | */ | 6 | */ |
| 15 | 7 | ||
| 16 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | 8 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
| @@ -1,16 +1,8 @@ | |||
| 1 | /* SPDX-License-Identifier: GPL-2.0 */ | ||
| 1 | /* | 2 | /* |
| 2 | * Intel(R) Trace Hub Global Trace Hub (GTH) data structures | 3 | * Intel(R) Trace Hub Global Trace Hub (GTH) data structures |
| 3 | * | 4 | * |
| 4 | * Copyright (C) 2014-2015 Intel Corporation. | 5 | * Copyright (C) 2014-2015 Intel 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 | */ | 6 | */ |
| 15 | 7 | ||
| 16 | #ifndef __INTEL_TH_GTH_H__ | 8 | #ifndef __INTEL_TH_GTH_H__ |
| @@ -1,16 +1,8 @@ | |||
| 1 | /* SPDX-License-Identifier: GPL-2.0 */ | ||
| 1 | /* | 2 | /* |
| 2 | * Intel(R) Trace Hub data structures | 3 | * Intel(R) Trace Hub data structures |
| 3 | * | 4 | * |
| 4 | * Copyright (C) 2014-2015 Intel Corporation. | 5 | * Copyright (C) 2014-2015 Intel 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 | */ | 6 | */ |
| 15 | 7 | ||
| 16 | #ifndef __INTEL_TH_H__ | 8 | #ifndef __INTEL_TH_H__ |
| @@ -50,9 +42,11 @@ struct intel_th_output { · 33 unmodified lines | |||
| 50 | /** | 42 | /** |
| 51 | * struct intel_th_drvdata - describes hardware capabilities and quirks | 43 | * struct intel_th_drvdata - describes hardware capabilities and quirks |
| 52 | * @tscu_enable: device needs SW to enable time stamping unit | 44 | * @tscu_enable: device needs SW to enable time stamping unit |
| 45 | * @host_mode_only: device can only operate in 'host debugger' mode | ||
| 53 | */ | 46 | */ |
| 54 | struct intel_th_drvdata { | 47 | struct intel_th_drvdata { |
| 55 | unsigned int tscu_enable : 1; | 48 | unsigned int tscu_enable : 1, |
| 49 | host_mode_only : 1; | ||
| 56 | }; | 50 | }; |
| 57 | 51 | ||
| 58 | #define INTEL_TH_CAP(_th, _cap) ((_th)->drvdata ? (_th)->drvdata->_cap : 0) | 52 | #define INTEL_TH_CAP(_th, _cap) ((_th)->drvdata ? (_th)->drvdata->_cap : 0) |
| @@ -1,16 +1,8 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 1 | /* | 2 | /* |
| 2 | * Intel(R) Trace Hub Memory Storage Unit | 3 | * Intel(R) Trace Hub Memory Storage Unit |
| 3 | * | 4 | * |
| 4 | * Copyright (C) 2014-2015 Intel Corporation. | 5 | * Copyright (C) 2014-2015 Intel 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 | */ | 6 | */ |
| 15 | 7 | ||
| 16 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | 8 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
| @@ -1,16 +1,8 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 1 | /* | 2 | /* |
| 2 | * Intel(R) Trace Hub Memory Storage Unit (MSU) data structures | 3 | * Intel(R) Trace Hub Memory Storage Unit (MSU) data structures |
| 3 | * | 4 | * |
| 4 | * Copyright (C) 2014-2015 Intel Corporation. | 5 | * Copyright (C) 2014-2015 Intel 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 | */ | 6 | */ |
| 15 | 7 | ||
| 16 | #ifndef __INTEL_TH_MSU_H__ | 8 | #ifndef __INTEL_TH_MSU_H__ |
| @@ -1,16 +1,8 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 1 | /* | 2 | /* |
| 2 | * Intel(R) Trace Hub pci driver | 3 | * Intel(R) Trace Hub pci driver |
| 3 | * | 4 | * |
| 4 | * Copyright (C) 2014-2015 Intel Corporation. | 5 | * Copyright (C) 2014-2015 Intel 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 | */ | 6 | */ |
| 15 | 7 | ||
| 16 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | 8 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
| @@ -1,16 +1,8 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 1 | /* | 2 | /* |
| 2 | * Intel(R) Trace Hub PTI output driver | 3 | * Intel(R) Trace Hub PTI output driver |
| 3 | * | 4 | * |
| 4 | * Copyright (C) 2014-2016 Intel Corporation. | 5 | * Copyright (C) 2014-2016 Intel 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 | */ | 6 | */ |
| 15 | 7 | ||
| 16 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | 8 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
| @@ -1,16 +1,8 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 1 | /* | 2 | /* |
| 2 | * Intel(R) Trace Hub PTI output data structures | 3 | * Intel(R) Trace Hub PTI output data structures |
| 3 | * | 4 | * |
| 4 | * Copyright (C) 2014-2015 Intel Corporation. | 5 | * Copyright (C) 2014-2015 Intel 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 | */ | 6 | */ |
| 15 | 7 | ||
| 16 | #ifndef __INTEL_TH_STH_H__ | 8 | #ifndef __INTEL_TH_STH_H__ |
| @@ -1,16 +1,8 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 1 | /* | 2 | /* |
| 2 | * Intel(R) Trace Hub Software Trace Hub support | 3 | * Intel(R) Trace Hub Software Trace Hub support |
| 3 | * | 4 | * |
| 4 | * Copyright (C) 2014-2015 Intel Corporation. | 5 | * Copyright (C) 2014-2015 Intel 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 | */ | 6 | */ |
| 15 | 7 | ||
| 16 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | 8 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
| @@ -1,16 +1,8 @@ | |||
| 1 | /* SPDX-License-Identifier: GPL-2.0 */ | ||
| 1 | /* | 2 | /* |
| 2 | * Intel(R) Trace Hub Software Trace Hub (STH) data structures | 3 | * Intel(R) Trace Hub Software Trace Hub (STH) data structures |
| 3 | * | 4 | * |
| 4 | * Copyright (C) 2014-2015 Intel Corporation. | 5 | * Copyright (C) 2014-2015 Intel 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 | */ | 6 | */ |
| 15 | 7 | ||
| 16 | #ifndef __INTEL_TH_STH_H__ | 8 | #ifndef __INTEL_TH_STH_H__ |
| @@ -1,16 +1,8 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 1 | /* | 2 | /* |
| 2 | * Simple kernel console driver for STM devices | 3 | * Simple kernel console driver for STM devices |
| 3 | * Copyright (c) 2014, Intel Corporation. | 4 | * Copyright (c) 2014, Intel Corporation. |
| 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 console will send kernel messages over STM devices to a trace host. | 6 | * STM console will send kernel messages over STM devices to a trace host. |
| 15 | */ | 7 | */ |
| 16 | 8 | ||
| @@ -1,16 +1,8 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 1 | /* | 2 | /* |
| 2 | * System Trace Module (STM) infrastructure | 3 | * System Trace Module (STM) infrastructure |
| 3 | * Copyright (c) 2014, Intel Corporation. | 4 | * Copyright (c) 2014, Intel Corporation. |
| 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 class implements generic infrastructure for System Trace Module devices | 6 | * STM class implements generic infrastructure for System Trace Module devices |
| 15 | * as defined in MIPI STPv2 specification. | 7 | * as defined in MIPI STPv2 specification. |
| 16 | */ | 8 | */ |
| @@ -1,16 +1,8 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 1 | /* | 2 | /* |
| 2 | * A dummy STM device for stm/stm_source class testing. | 3 | * A dummy STM device for stm/stm_source class testing. |
| 3 | * Copyright (c) 2014, Intel Corporation. | 4 | * Copyright (c) 2014, Intel Corporation. |
| 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 class implements generic infrastructure for System Trace Module devices | 6 | * STM class implements generic infrastructure for System Trace Module devices |
| 15 | * as defined in MIPI STPv2 specification. | 7 | * as defined in MIPI STPv2 specification. |
| 16 | */ | 8 | */ |
| @@ -20,6 +12,7 @@ · 3 unmodified lines | |||
| 20 | #include <linux/module.h> | 12 | #include <linux/module.h> |
| 21 | #include <linux/slab.h> | 13 | #include <linux/slab.h> |
| 22 | #include <linux/stm.h> | 14 | #include <linux/stm.h> |
| 15 | #include <uapi/linux/stm.h> | ||
| 23 | 16 | ||
| 24 | static ssize_t notrace | 17 | static ssize_t notrace |
| 25 | dummy_stm_packet(struct stm_data *stm_data, unsigned int master, | 18 | dummy_stm_packet(struct stm_data *stm_data, unsigned int master, |
| @@ -52,6 +45,18 @@ static unsigned int fail_mode; · 26 unmodified lines | |||
| 52 | 45 | ||
| 53 | module_param(fail_mode, int, 0600); | 46 | module_param(fail_mode, int, 0600); |
| 54 | 47 | ||
| 48 | static unsigned int master_min; | ||
| 49 | |||
| 50 | module_param(master_min, int, 0400); | ||
| 51 | |||
| 52 | static unsigned int master_max = STP_MASTER_MAX; | ||
| 53 | |||
| 54 | module_param(master_max, int, 0400); | ||
| 55 | |||
| 56 | static unsigned int nr_channels = STP_CHANNEL_MAX; | ||
| 57 | |||
| 58 | module_param(nr_channels, int, 0400); | ||
| 59 | |||
| 55 | static int dummy_stm_link(struct stm_data *data, unsigned int master, | 60 | static int dummy_stm_link(struct stm_data *data, unsigned int master, |
| 56 | unsigned int channel) | 61 | unsigned int channel) |
| 57 | { | 62 | { |
| @@ -68,14 +73,19 @@ static int dummy_stm_init(void) · 10 unmodified lines | |||
| 68 | if (nr_dummies < 0 || nr_dummies > DUMMY_STM_MAX) | 73 | if (nr_dummies < 0 || nr_dummies > DUMMY_STM_MAX) |
| 69 | return -EINVAL; | 74 | return -EINVAL; |
| 70 | 75 | ||
| 76 | if (master_min > master_max || | ||
| 77 | master_max > STP_MASTER_MAX || | ||
| 78 | nr_channels > STP_CHANNEL_MAX) | ||
| 79 | return -EINVAL; | ||
| 80 | |||
| 71 | for (i = 0; i < nr_dummies; i++) { | 81 | for (i = 0; i < nr_dummies; i++) { |
| 72 | dummy_stm[i].name = kasprintf(GFP_KERNEL, "dummy_stm.%d", i); | 82 | dummy_stm[i].name = kasprintf(GFP_KERNEL, "dummy_stm.%d", i); |
| 73 | if (!dummy_stm[i].name) | 83 | if (!dummy_stm[i].name) |
| 74 | goto fail_unregister; | 84 | goto fail_unregister; |
| 75 | 85 | ||
| 76 | dummy_stm[i].sw_start = 0x0000; | 86 | dummy_stm[i].sw_start = master_min; |
| 77 | dummy_stm[i].sw_end = 0xffff; | 87 | dummy_stm[i].sw_end = master_max; |
| 78 | dummy_stm[i].sw_nchannels = 0xffff; | 88 | dummy_stm[i].sw_nchannels = nr_channels; |
| 79 | dummy_stm[i].packet = dummy_stm_packet; | 89 | dummy_stm[i].packet = dummy_stm_packet; |
| 80 | dummy_stm[i].link = dummy_stm_link; | 90 | dummy_stm[i].link = dummy_stm_link; |
| 81 | 91 | ||
| @@ -1,16 +1,8 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 1 | /* | 2 | /* |
| 2 | * Simple heartbeat STM source driver | 3 | * Simple heartbeat STM source driver |
| 3 | * Copyright (c) 2016, Intel Corporation. | 4 | * Copyright (c) 2016, Intel Corporation. |
| 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 | * Heartbeat STM source will send repetitive messages over STM devices to a | 6 | * Heartbeat STM source will send repetitive messages over STM devices to a |
| 15 | * trace host. | 7 | * trace host. |
| 16 | */ | 8 | */ |
| @@ -1,16 +1,8 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 1 | /* | 2 | /* |
| 2 | * System Trace Module (STM) master/channel allocation policy management | 3 | * System Trace Module (STM) master/channel allocation policy management |
| 3 | * Copyright (c) 2014, Intel Corporation. | 4 | * Copyright (c) 2014, Intel Corporation. |
| 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 | * A master/channel allocation policy allows mapping string identifiers to | 6 | * A master/channel allocation policy allows mapping string identifiers to |
| 15 | * master and channel ranges, where allocation can be done. | 7 | * master and channel ranges, where allocation can be done. |
| 16 | */ | 8 | */ |
| @@ -1,16 +1,8 @@ | |||
| 1 | /* SPDX-License-Identifier: GPL-2.0 */ | ||
| 1 | /* | 2 | /* |
| 2 | * System Trace Module (STM) infrastructure | 3 | * System Trace Module (STM) infrastructure |
| 3 | * Copyright (c) 2014, Intel Corporation. | 4 | * Copyright (c) 2014, Intel Corporation. |
| 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 class implements generic infrastructure for System Trace Module devices | 6 | * STM class implements generic infrastructure for System Trace Module devices |
| 15 | * as defined in MIPI STPv2 specification. | 7 | * as defined in MIPI STPv2 specification. |
| 16 | */ | 8 | */ |
| @@ -117,6 +117,7 @@ static void mcb_pci_remove(struct pci_dev *pdev) · 116 unmodified lines | |||
| 117 | 117 | ||
| 118 | static const struct pci_device_id mcb_pci_tbl[] = { | 118 | static const struct pci_device_id mcb_pci_tbl[] = { |
| 119 | { PCI_DEVICE(PCI_VENDOR_ID_MEN, PCI_DEVICE_ID_MEN_CHAMELEON) }, | 119 | { PCI_DEVICE(PCI_VENDOR_ID_MEN, PCI_DEVICE_ID_MEN_CHAMELEON) }, |
| 120 | { PCI_DEVICE(PCI_VENDOR_ID_ALTERA, PCI_DEVICE_ID_MEN_CHAMELEON) }, | ||
| 120 | { 0 }, | 121 | { 0 }, |
| 121 | }; | 122 | }; |
| 122 | MODULE_DEVICE_TABLE(pci, mcb_pci_tbl); | 123 | MODULE_DEVICE_TABLE(pci, mcb_pci_tbl); |
| @@ -75,7 +75,6 @@ config ATMEL_TCB_CLKSRC · 74 unmodified lines | |||
| 75 | config ATMEL_TCB_CLKSRC_BLOCK | 75 | config ATMEL_TCB_CLKSRC_BLOCK |
| 76 | int | 76 | int |
| 77 | depends on ATMEL_TCB_CLKSRC | 77 | depends on ATMEL_TCB_CLKSRC |
| 78 | prompt "TC Block" if CPU_AT32AP700X | ||
| 79 | default 0 | 78 | default 0 |
| 80 | range 0 1 | 79 | range 0 1 |
| 81 | help | 80 | help |
| @@ -12,7 +12,7 @@ obj-$(CONFIG_ATMEL_SSC) += atmel-ssc.o · 11 unmodified lines | |||
| 12 | obj-$(CONFIG_ATMEL_TCLIB) += atmel_tclib.o | 12 | obj-$(CONFIG_ATMEL_TCLIB) += atmel_tclib.o |
| 13 | obj-$(CONFIG_DUMMY_IRQ) += dummy-irq.o | 13 | obj-$(CONFIG_DUMMY_IRQ) += dummy-irq.o |
| 14 | obj-$(CONFIG_ICS932S401) += ics932s401.o | 14 | obj-$(CONFIG_ICS932S401) += ics932s401.o |
| 15 | obj-$(CONFIG_LKDTM) += lkdtm.o | 15 | obj-$(CONFIG_LKDTM) += lkdtm/ |
| 16 | obj-$(CONFIG_TIFM_CORE) += tifm_core.o | 16 | obj-$(CONFIG_TIFM_CORE) += tifm_core.o |
| 17 | obj-$(CONFIG_TIFM_7XX1) += tifm_7xx1.o | 17 | obj-$(CONFIG_TIFM_7XX1) += tifm_7xx1.o |
| 18 | obj-$(CONFIG_PHANTOM) += phantom.o | 18 | obj-$(CONFIG_PHANTOM) += phantom.o |
| @@ -57,21 +57,3 @@ obj-$(CONFIG_ASPEED_LPC_SNOOP) += aspeed-lpc-snoop.o · 38 unmodified lines | |||
| 57 | obj-$(CONFIG_PCI_ENDPOINT_TEST) += pci_endpoint_test.o | 57 | obj-$(CONFIG_PCI_ENDPOINT_TEST) += pci_endpoint_test.o |
| 58 | obj-$(CONFIG_OCXL) += ocxl/ | 58 | obj-$(CONFIG_OCXL) += ocxl/ |
| 59 | obj-$(CONFIG_MISC_RTSX) += cardreader/ | 59 | obj-$(CONFIG_MISC_RTSX) += cardreader/ |
| 60 | |||
| 61 | lkdtm-$(CONFIG_LKDTM) += lkdtm_core.o | ||
| 62 | lkdtm-$(CONFIG_LKDTM) += lkdtm_bugs.o | ||
| 63 | lkdtm-$(CONFIG_LKDTM) += lkdtm_heap.o | ||
| 64 | lkdtm-$(CONFIG_LKDTM) += lkdtm_perms.o | ||
| 65 | lkdtm-$(CONFIG_LKDTM) += lkdtm_refcount.o | ||
| 66 | lkdtm-$(CONFIG_LKDTM) += lkdtm_rodata_objcopy.o | ||
| 67 | lkdtm-$(CONFIG_LKDTM) += lkdtm_usercopy.o | ||
| 68 | |||
| 69 | KCOV_INSTRUMENT_lkdtm_rodata.o := n | ||
| 70 | |||
| 71 | OBJCOPYFLAGS := | ||
| 72 | OBJCOPYFLAGS_lkdtm_rodata_objcopy.o := \ | ||
| 73 | --set-section-flags .text=alloc,readonly \ | ||
| 74 | --rename-section .text=.rodata | ||
| 75 | targets += lkdtm_rodata.o lkdtm_rodata_objcopy.o | ||
| 76 | $(obj)/lkdtm_rodata_objcopy.o: $(obj)/lkdtm_rodata.o FORCE | ||
| 77 | $(call if_changed,objcopy) | ||
| @@ -7,6 +7,7 @@ · 6 unmodified lines | |||
| 7 | * 2 of the License, or (at your option) any later version. | 7 | * 2 of the License, or (at your option) any later version. |
| 8 | */ | 8 | */ |
| 9 | 9 | ||
| 10 | #include <linux/clk.h> | ||
| 10 | #include <linux/mfd/syscon.h> | 11 | #include <linux/mfd/syscon.h> |
| 11 | #include <linux/miscdevice.h> | 12 | #include <linux/miscdevice.h> |
| 12 | #include <linux/mm.h> | 13 | #include <linux/mm.h> |
| @@ -20,12 +21,17 @@ · 7 unmodified lines | |||
| 20 | 21 | ||
| 21 | #define DEVICE_NAME "aspeed-lpc-ctrl" | 22 | #define DEVICE_NAME "aspeed-lpc-ctrl" |
| 22 | 23 | ||
| 24 | #define HICR5 0x0 | ||
| 25 | #define HICR5_ENL2H BIT(8) | ||
| 26 | #define HICR5_ENFWH BIT(10) | ||
| 27 | |||
| 23 | #define HICR7 0x8 | 28 | #define HICR7 0x8 |
| 24 | #define HICR8 0xc | 29 | #define HICR8 0xc |
| 25 | 30 | ||
| 26 | struct aspeed_lpc_ctrl { | 31 | struct aspeed_lpc_ctrl { |
| 27 | struct miscdevice miscdev; | 32 | struct miscdevice miscdev; |
| 28 | struct regmap *regmap; | 33 | struct regmap *regmap; |
| 34 | struct clk *clk; | ||
| 29 | phys_addr_t mem_base; | 35 | phys_addr_t mem_base; |
| 30 | resource_size_t mem_size; | 36 | resource_size_t mem_size; |
| 31 | u32 pnor_size; | 37 | u32 pnor_size; |
| @@ -153,8 +159,18 @@ static long aspeed_lpc_ctrl_ioctl(struct file *file, unsigned int cmd, · 121 unmodified lines | |||
| 153 | if (rc) | 159 | if (rc) |
| 154 | return rc; | 160 | return rc; |
| 155 | 161 | ||
| 156 | return regmap_write(lpc_ctrl->regmap, HICR8, | 162 | rc = regmap_write(lpc_ctrl->regmap, HICR8, |
| 157 | (~(map.size - 1)) | ((map.size >> 16) - 1)); | 163 | (~(map.size - 1)) | ((map.size >> 16) - 1)); |
| 164 | if (rc) | ||
| 165 | return rc; | ||
| 166 | |||
| 167 | /* | ||
| 168 | * Enable LPC FHW cycles. This is required for the host to | ||
| 169 | * access the regions specified. | ||
| 170 | */ | ||
| 171 | return regmap_update_bits(lpc_ctrl->regmap, HICR5, | ||
| 172 | HICR5_ENFWH | HICR5_ENL2H, | ||
| 173 | HICR5_ENFWH | HICR5_ENL2H); | ||
| 158 | } | 174 | } |
| 159 | 175 | ||
| 160 | return -EINVAL; | 176 | return -EINVAL; |
| @@ -221,16 +237,33 @@ static int aspeed_lpc_ctrl_probe(struct platform_device *pdev) · 60 unmodified lines | |||
| 221 | return -ENODEV; | 237 | return -ENODEV; |
| 222 | } | 238 | } |
| 223 | 239 | ||
| 240 | lpc_ctrl->clk = devm_clk_get(dev, NULL); | ||
| 241 | if (IS_ERR(lpc_ctrl->clk)) { | ||
| 242 | dev_err(dev, "couldn't get clock\n"); | ||
| 243 | return PTR_ERR(lpc_ctrl->clk); | ||
| 244 | } | ||
| 245 | rc = clk_prepare_enable(lpc_ctrl->clk); | ||
| 246 | if (rc) { | ||
| 247 | dev_err(dev, "couldn't enable clock\n"); | ||
| 248 | return rc; | ||
| 249 | } | ||
| 250 | |||
| 224 | lpc_ctrl->miscdev.minor = MISC_DYNAMIC_MINOR; | 251 | lpc_ctrl->miscdev.minor = MISC_DYNAMIC_MINOR; |
| 225 | lpc_ctrl->miscdev.name = DEVICE_NAME; | 252 | lpc_ctrl->miscdev.name = DEVICE_NAME; |
| 226 | lpc_ctrl->miscdev.fops = &aspeed_lpc_ctrl_fops; | 253 | lpc_ctrl->miscdev.fops = &aspeed_lpc_ctrl_fops; |
| 227 | lpc_ctrl->miscdev.parent = dev; | 254 | lpc_ctrl->miscdev.parent = dev; |
| 228 | rc = misc_register(&lpc_ctrl->miscdev); | 255 | rc = misc_register(&lpc_ctrl->miscdev); |
| 229 | if (rc) | 256 | if (rc) { |
| 230 | dev_err(dev, "Unable to register device\n"); | 257 | dev_err(dev, "Unable to register device\n"); |
| 231 | else | 258 | goto err; |
| 232 | dev_info(dev, "Loaded at %pr\n", &resm); | 259 | } |
| 260 | |||
| 261 | dev_info(dev, "Loaded at %pr\n", &resm); | ||
| 262 | |||
| 263 | return 0; | ||
| 233 | 264 | ||
| 265 | err: | ||
| 266 | clk_disable_unprepare(lpc_ctrl->clk); | ||
| 234 | return rc; | 267 | return rc; |
| 235 | } | 268 | } |
| 236 | 269 | ||
| @@ -239,6 +272,7 @@ static int aspeed_lpc_ctrl_remove(struct platform_device *pdev) · 2 unmodified lines | |||
| 239 | struct aspeed_lpc_ctrl *lpc_ctrl = dev_get_drvdata(&pdev->dev); | 272 | struct aspeed_lpc_ctrl *lpc_ctrl = dev_get_drvdata(&pdev->dev); |
| 240 | 273 | ||
| 241 | misc_deregister(&lpc_ctrl->miscdev); | 274 | misc_deregister(&lpc_ctrl->miscdev); |
| 275 | clk_disable_unprepare(lpc_ctrl->clk); | ||
| 242 | 276 | ||
| 243 | return 0; | 277 | return 0; |
| 244 | } | 278 | } |
| @@ -388,17 +388,17 @@ static void rts5260_disable_ocp(struct rtsx_pcr *pcr) · 387 unmodified lines | |||
| 388 | OC_POWER_DOWN); | 388 | OC_POWER_DOWN); |
| 389 | } | 389 | } |
| 390 | 390 | ||
| 391 | int rts5260_get_ocpstat(struct rtsx_pcr *pcr, u8 *val) | 391 | static int rts5260_get_ocpstat(struct rtsx_pcr *pcr, u8 *val) |
| 392 | { | 392 | { |
| 393 | return rtsx_pci_read_register(pcr, REG_OCPSTAT, val); | 393 | return rtsx_pci_read_register(pcr, REG_OCPSTAT, val); |
| 394 | } | 394 | } |
| 395 | 395 | ||
| 396 | int rts5260_get_ocpstat2(struct rtsx_pcr *pcr, u8 *val) | 396 | static int rts5260_get_ocpstat2(struct rtsx_pcr *pcr, u8 *val) |
| 397 | { | 397 | { |
| 398 | return rtsx_pci_read_register(pcr, REG_DV3318_OCPSTAT, val); | 398 | return rtsx_pci_read_register(pcr, REG_DV3318_OCPSTAT, val); |
| 399 | } | 399 | } |
| 400 | 400 | ||
| 401 | void rts5260_clear_ocpstat(struct rtsx_pcr *pcr) | 401 | static void rts5260_clear_ocpstat(struct rtsx_pcr *pcr) |
| 402 | { | 402 | { |
| 403 | u8 mask = 0; | 403 | u8 mask = 0; |
| 404 | u8 val = 0; | 404 | u8 val = 0; |
| @@ -418,7 +418,7 @@ void rts5260_clear_ocpstat(struct rtsx_pcr *pcr) · 13 unmodified lines | |||
| 418 | DV3318_OCP_INT_CLR | DV3318_OCP_CLR, 0); | 418 | DV3318_OCP_INT_CLR | DV3318_OCP_CLR, 0); |
| 419 | } | 419 | } |
| 420 | 420 | ||
| 421 | void rts5260_process_ocp(struct rtsx_pcr *pcr) | 421 | static void rts5260_process_ocp(struct rtsx_pcr *pcr) |
| 422 | { | 422 | { |
| 423 | if (!pcr->option.ocp_en) | 423 | if (!pcr->option.ocp_en) |
| 424 | return; | 424 | return; |
| @@ -449,7 +449,7 @@ void rts5260_process_ocp(struct rtsx_pcr *pcr) · 24 unmodified lines | |||
| 449 | } | 449 | } |
| 450 | } | 450 | } |
| 451 | 451 | ||
| 452 | int rts5260_init_hw(struct rtsx_pcr *pcr) | 452 | static int rts5260_init_hw(struct rtsx_pcr *pcr) |
| 453 | { | 453 | { |
| 454 | int err; | 454 | int err; |
| 455 | 455 | ||
| @@ -620,7 +620,7 @@ static int rts5260_extra_init_hw(struct rtsx_pcr *pcr) · 164 unmodified lines | |||
| 620 | return 0; | 620 | return 0; |
| 621 | } | 621 | } |
| 622 | 622 | ||
| 623 | void rts5260_set_aspm(struct rtsx_pcr *pcr, bool enable) | 623 | static void rts5260_set_aspm(struct rtsx_pcr *pcr, bool enable) |
| 624 | { | 624 | { |
| 625 | struct rtsx_cr_option *option = &pcr->option; | 625 | struct rtsx_cr_option *option = &pcr->option; |
| 626 | u8 val = 0; | 626 | u8 val = 0; |
| @@ -1,14 +1,11 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0+ | ||
| 1 | /* | 2 | /* |
| 2 | * at24.c - handle most I2C EEPROMs | 3 | * at24.c - handle most I2C EEPROMs |
| 3 | * | 4 | * |
| 4 | * Copyright (C) 2005-2007 David Brownell | 5 | * Copyright (C) 2005-2007 David Brownell |
| 5 | * Copyright (C) 2008 Wolfram Sang, Pengutronix | 6 | * Copyright (C) 2008 Wolfram Sang, Pengutronix |
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or modify | ||
| 8 | * it under the terms of the GNU General Public License as published by | ||
| 9 | * the Free Software Foundation; either version 2 of the License, or | ||
| 10 | * (at your option) any later version. | ||
| 11 | */ | 7 | */ |
| 8 | |||
| 12 | #include <linux/kernel.h> | 9 | #include <linux/kernel.h> |
| 13 | #include <linux/init.h> | 10 | #include <linux/init.h> |
| 14 | #include <linux/module.h> | 11 | #include <linux/module.h> |
| @@ -63,8 +60,6 @@ struct at24_client { · 48 unmodified lines | |||
| 63 | }; | 60 | }; |
| 64 | 61 | ||
| 65 | struct at24_data { | 62 | struct at24_data { |
| 66 | struct at24_platform_data chip; | ||
| 67 | |||
| 68 | /* | 63 | /* |
| 69 | * Lock protects against activities from other Linux tasks, | 64 | * Lock protects against activities from other Linux tasks, |
| 70 | * but not from changes by other I2C masters. | 65 | * but not from changes by other I2C masters. |
| @@ -75,7 +70,10 @@ struct at24_data { · 4 unmodified lines | |||
| 75 | unsigned int num_addresses; | 70 | unsigned int num_addresses; |
| 76 | unsigned int offset_adj; | 71 | unsigned int offset_adj; |
| 77 | 72 | ||
| 78 | struct nvmem_config nvmem_config; | 73 | u32 byte_len; |
| 74 | u16 page_size; | ||
| 75 | u8 flags; | ||
| 76 | |||
| 79 | struct nvmem_device *nvmem; | 77 | struct nvmem_device *nvmem; |
| 80 | 78 | ||
| 81 | struct gpio_desc *wp_gpio; | 79 | struct gpio_desc *wp_gpio; |
| @@ -239,8 +237,6 @@ static const struct acpi_device_id at24_acpi_ids[] = { · 157 unmodified lines | |||
| 239 | }; | 237 | }; |
| 240 | MODULE_DEVICE_TABLE(acpi, at24_acpi_ids); | 238 | MODULE_DEVICE_TABLE(acpi, at24_acpi_ids); |
| 241 | 239 | ||
| 242 | /*-------------------------------------------------------------------------*/ | ||
| 243 | |||
| 244 | /* | 240 | /* |
| 245 | * This routine supports chips which consume multiple I2C addresses. It | 241 | * This routine supports chips which consume multiple I2C addresses. It |
| 246 | * computes the addressing information to be used for a given r/w request. | 242 | * computes the addressing information to be used for a given r/w request. |
| @@ -255,7 +251,7 @@ static struct at24_client *at24_translate_offset(struct at24_data *at24, · 8 unmodified lines | |||
| 255 | { | 251 | { |
| 256 | unsigned int i; | 252 | unsigned int i; |
| 257 | 253 | ||
| 258 | if (at24->chip.flags & AT24_FLAG_ADDR16) { | 254 | if (at24->flags & AT24_FLAG_ADDR16) { |
| 259 | i = *offset >> 16; | 255 | i = *offset >> 16; |
| 260 | *offset &= 0xffff; | 256 | *offset &= 0xffff; |
| 261 | } else { | 257 | } else { |
| @@ -266,6 +262,11 @@ static struct at24_client *at24_translate_offset(struct at24_data *at24, · 4 unmodified lines | |||
| 266 | return &at24->client[i]; | 262 | return &at24->client[i]; |
| 267 | } | 263 | } |
| 268 | 264 | ||
| 265 | static struct device *at24_base_client_dev(struct at24_data *at24) | ||
| 266 | { | ||
| 267 | return &at24->client[0].client->dev; | ||
| 268 | } | ||
| 269 | |||
| 269 | static size_t at24_adjust_read_count(struct at24_data *at24, | 270 | static size_t at24_adjust_read_count(struct at24_data *at24, |
| 270 | unsigned int offset, size_t count) | 271 | unsigned int offset, size_t count) |
| 271 | { | 272 | { |
| @@ -277,8 +278,8 @@ static size_t at24_adjust_read_count(struct at24_data *at24, · 5 unmodified lines | |||
| 277 | * the next slave address: truncate the count to the slave boundary, | 278 | * the next slave address: truncate the count to the slave boundary, |
| 278 | * so that the read never straddles slaves. | 279 | * so that the read never straddles slaves. |
| 279 | */ | 280 | */ |
| 280 | if (at24->chip.flags & AT24_FLAG_NO_RDROL) { | 281 | if (at24->flags & AT24_FLAG_NO_RDROL) { |
| 281 | bits = (at24->chip.flags & AT24_FLAG_ADDR16) ? 16 : 8; | 282 | bits = (at24->flags & AT24_FLAG_ADDR16) ? 16 : 8; |
| 282 | remainder = BIT(bits) - offset; | 283 | remainder = BIT(bits) - offset; |
| 283 | if (count > remainder) | 284 | if (count > remainder) |
| 284 | count = remainder; | 285 | count = remainder; |
| @@ -337,7 +338,7 @@ static size_t at24_adjust_write_count(struct at24_data *at24, · 52 unmodified lines | |||
| 337 | count = at24->write_max; | 338 | count = at24->write_max; |
| 338 | 339 | ||
| 339 | /* Never roll over backwards, to the start of this page */ | 340 | /* Never roll over backwards, to the start of this page */ |
| 340 | next_page = roundup(offset + 1, at24->chip.page_size); | 341 | next_page = roundup(offset + 1, at24->page_size); |
| 341 | if (offset + count > next_page) | 342 | if (offset + count > next_page) |
| 342 | count = next_page - offset; | 343 | count = next_page - offset; |
| 343 | 344 | ||
| @@ -371,15 +372,18 @@ static ssize_t at24_regmap_write(struct at24_data *at24, const char *buf, · 27 unmodified lines | |||
| 371 | 372 | ||
| 372 | static int at24_read(void *priv, unsigned int off, void *val, size_t count) | 373 | static int at24_read(void *priv, unsigned int off, void *val, size_t count) |
| 373 | { | 374 | { |
| 374 | struct at24_data *at24 = priv; | 375 | struct at24_data *at24; |
| 375 | struct device *dev = &at24->client[0].client->dev; | 376 | struct device *dev; |
| 376 | char *buf = val; | 377 | char *buf = val; |
| 377 | int ret; | 378 | int ret; |
| 378 | 379 | ||
| 380 | at24 = priv; | ||
| 381 | dev = at24_base_client_dev(at24); | ||
| 382 | |||
| 379 | if (unlikely(!count)) | 383 | if (unlikely(!count)) |
| 380 | return count; | 384 | return count; |
| 381 | 385 | ||
| 382 | if (off + count > at24->chip.byte_len) | 386 | if (off + count > at24->byte_len) |
| 383 | return -EINVAL; | 387 | return -EINVAL; |
| 384 | 388 | ||
| 385 | ret = pm_runtime_get_sync(dev); | 389 | ret = pm_runtime_get_sync(dev); |
| @@ -395,17 +399,15 @@ static int at24_read(void *priv, unsigned int off, void *val, size_t count) · 9 unmodified lines | |||
| 395 | mutex_lock(&at24->lock); | 399 | mutex_lock(&at24->lock); |
| 396 | 400 | ||
| 397 | while (count) { | 401 | while (count) { |
| 398 | int status; | 402 | ret = at24_regmap_read(at24, buf, off, count); |
| 399 | 403 | if (ret < 0) { | |
| 400 | status = at24_regmap_read(at24, buf, off, count); | ||
| 401 | if (status < 0) { | ||
| 402 | mutex_unlock(&at24->lock); | 404 | mutex_unlock(&at24->lock); |
| 403 | pm_runtime_put(dev); | 405 | pm_runtime_put(dev); |
| 404 | return status; | 406 | return ret; |
| 405 | } | 407 | } |
| 406 | buf += status; | 408 | buf += ret; |
| 407 | off += status; | 409 | off += ret; |
| 408 | count -= status; | 410 | count -= ret; |
| 409 | } | 411 | } |
| 410 | 412 | ||
| 411 | mutex_unlock(&at24->lock); | 413 | mutex_unlock(&at24->lock); |
| @@ -417,15 +419,18 @@ static int at24_read(void *priv, unsigned int off, void *val, size_t count) · 5 unmodified lines | |||
| 417 | 419 | ||
| 418 | static int at24_write(void *priv, unsigned int off, void *val, size_t count) | 420 | static int at24_write(void *priv, unsigned int off, void *val, size_t count) |
| 419 | { | 421 | { |
| 420 | struct at24_data *at24 = priv; | 422 | struct at24_data *at24; |
| 421 | struct device *dev = &at24->client[0].client->dev; | 423 | struct device *dev; |
| 422 | char *buf = val; | 424 | char *buf = val; |
| 423 | int ret; | 425 | int ret; |
| 424 | 426 | ||
| 427 | at24 = priv; | ||
| 428 | dev = at24_base_client_dev(at24); | ||
| 429 | |||
| 425 | if (unlikely(!count)) | 430 | if (unlikely(!count)) |
| 426 | return -EINVAL; | 431 | return -EINVAL; |
| 427 | 432 | ||
| 428 | if (off + count > at24->chip.byte_len) | 433 | if (off + count > at24->byte_len) |
| 429 | return -EINVAL; | 434 | return -EINVAL; |
| 430 | 435 | ||
| 431 | ret = pm_runtime_get_sync(dev); | 436 | ret = pm_runtime_get_sync(dev); |
| @@ -442,18 +447,16 @@ static int at24_write(void *priv, unsigned int off, void *val, size_t count) · 10 unmodified lines | |||
| 442 | gpiod_set_value_cansleep(at24->wp_gpio, 0); | 447 | gpiod_set_value_cansleep(at24->wp_gpio, 0); |
| 443 | 448 | ||
| 444 | while (count) { | 449 | while (count) { |
| 445 | int status; | 450 | ret = at24_regmap_write(at24, buf, off, count); |
| 446 | 451 | if (ret < 0) { | |
| 447 | status = at24_regmap_write(at24, buf, off, count); | ||
| 448 | if (status < 0) { | ||
| 449 | gpiod_set_value_cansleep(at24->wp_gpio, 1); | 452 | gpiod_set_value_cansleep(at24->wp_gpio, 1); |
| 450 | mutex_unlock(&at24->lock); | 453 | mutex_unlock(&at24->lock); |
| 451 | pm_runtime_put(dev); | 454 | pm_runtime_put(dev); |
| 452 | return status; | 455 | return ret; |
| 453 | } | 456 | } |
| 454 | buf += status; | 457 | buf += ret; |
| 455 | off += status; | 458 | off += ret; |
| 456 | count -= status; | 459 | count -= ret; |
| 457 | } | 460 | } |
| 458 | 461 | ||
| 459 | gpiod_set_value_cansleep(at24->wp_gpio, 1); | 462 | gpiod_set_value_cansleep(at24->wp_gpio, 1); |
| @@ -464,7 +467,8 @@ static int at24_write(void *priv, unsigned int off, void *val, size_t count) · 4 unmodified lines | |||
| 464 | return 0; | 467 | return 0; |
| 465 | } | 468 | } |
| 466 | 469 | ||
| 467 | static void at24_get_pdata(struct device *dev, struct at24_platform_data *chip) | 470 | static void at24_properties_to_pdata(struct device *dev, |
| 471 | struct at24_platform_data *chip) | ||
| 468 | { | 472 | { |
| 469 | int err; | 473 | int err; |
| 470 | u32 val; | 474 | u32 val; |
| @@ -491,6 +495,43 @@ static void at24_get_pdata(struct device *dev, struct at24_platform_data *chip) · 20 unmodified lines | |||
| 491 | } | 495 | } |
| 492 | } | 496 | } |
| 493 | 497 | ||
| 498 | static int at24_get_pdata(struct device *dev, struct at24_platform_data *pdata) | ||
| 499 | { | ||
| 500 | struct device_node *of_node = dev->of_node; | ||
| 501 | const struct at24_chip_data *cdata; | ||
| 502 | const struct i2c_device_id *id; | ||
| 503 | struct at24_platform_data *pd; | ||
| 504 | |||
| 505 | pd = dev_get_platdata(dev); | ||
| 506 | if (pd) { | ||
| 507 | memcpy(pdata, pd, sizeof(*pdata)); | ||
| 508 | return 0; | ||
| 509 | } | ||
| 510 | |||
| 511 | id = i2c_match_id(at24_ids, to_i2c_client(dev)); | ||
| 512 | |||
| 513 | /* | ||
| 514 | * The I2C core allows OF nodes compatibles to match against the | ||
| 515 | * I2C device ID table as a fallback, so check not only if an OF | ||
| 516 | * node is present but also if it matches an OF device ID entry. | ||
| 517 | */ | ||
| 518 | if (of_node && of_match_device(at24_of_match, dev)) | ||
| 519 | cdata = of_device_get_match_data(dev); | ||
| 520 | else if (id) | ||
| 521 | cdata = (void *)&id->driver_data; | ||
| 522 | else | ||
| 523 | cdata = acpi_device_get_match_data(dev); | ||
| 524 | |||
| 525 | if (!cdata) | ||
| 526 | return -ENODEV; | ||
| 527 | |||
| 528 | pdata->byte_len = cdata->byte_len; | ||
| 529 | pdata->flags = cdata->flags; | ||
| 530 | at24_properties_to_pdata(dev, pdata); | ||
| 531 | |||
| 532 | return 0; | ||
| 533 | } | ||
| 534 | |||
| 494 | static unsigned int at24_get_offset_adj(u8 flags, unsigned int byte_len) | 535 | static unsigned int at24_get_offset_adj(u8 flags, unsigned int byte_len) |
| 495 | { | 536 | { |
| 496 | if (flags & AT24_FLAG_MAC) { | 537 | if (flags & AT24_FLAG_MAC) { |
| @@ -514,102 +555,83 @@ static unsigned int at24_get_offset_adj(u8 flags, unsigned int byte_len) · 17 unmodified lines | |||
| 514 | } | 555 | } |
| 515 | } | 556 | } |
| 516 | 557 | ||
| 517 | static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id) | 558 | static int at24_probe(struct i2c_client *client) |
| 518 | { | 559 | { |
| 519 | struct at24_platform_data chip = { 0 }; | ||
| 520 | const struct at24_chip_data *cd = NULL; | ||
| 521 | bool writable; | ||
| 522 | struct at24_data *at24; | ||
| 523 | int err; | ||
| 524 | unsigned int i, num_addresses; | ||
| 525 | struct regmap_config regmap_config = { }; | 560 | struct regmap_config regmap_config = { }; |
| 561 | struct nvmem_config nvmem_config = { }; | ||
| 562 | struct at24_platform_data pdata = { }; | ||
| 563 | struct device *dev = &client->dev; | ||
| 564 | bool i2c_fn_i2c, i2c_fn_block; | ||
| 565 | unsigned int i, num_addresses; | ||
| 566 | struct at24_data *at24; | ||
| 567 | struct regmap *regmap; | ||
| 568 | size_t at24_size; | ||
| 569 | bool writable; | ||
| 526 | u8 test_byte; | 570 | u8 test_byte; |
| 571 | int err; | ||
| 527 | 572 | ||
| 528 | if (client->dev.platform_data) { | 573 | i2c_fn_i2c = i2c_check_functionality(client->adapter, I2C_FUNC_I2C); |
| 529 | chip = *(struct at24_platform_data *)client->dev.platform_data; | 574 | i2c_fn_block = i2c_check_functionality(client->adapter, |
| 530 | } else { | 575 | I2C_FUNC_SMBUS_WRITE_I2C_BLOCK); |
| 531 | /* | ||
| 532 | * The I2C core allows OF nodes compatibles to match against the | ||
| 533 | * I2C device ID table as a fallback, so check not only if an OF | ||
| 534 | * node is present but also if it matches an OF device ID entry. | ||
| 535 | */ | ||
| 536 | if (client->dev.of_node && | ||
| 537 | of_match_device(at24_of_match, &client->dev)) { | ||
| 538 | cd = of_device_get_match_data(&client->dev); | ||
| 539 | } else if (id) { | ||
| 540 | cd = (void *)id->driver_data; | ||
| 541 | } else { | ||
| 542 | const struct acpi_device_id *aid; | ||
| 543 | |||
| 544 | aid = acpi_match_device(at24_acpi_ids, &client->dev); | ||
| 545 | if (aid) | ||
| 546 | cd = (void *)aid->driver_data; | ||
| 547 | } | ||
| 548 | if (!cd) | ||
| 549 | return -ENODEV; | ||
| 550 | 576 | ||
| 551 | chip.byte_len = cd->byte_len; | 577 | err = at24_get_pdata(dev, &pdata); |
| 552 | chip.flags = cd->flags; | 578 | if (err) |
| 553 | at24_get_pdata(&client->dev, &chip); | 579 | return err; |
| 554 | } | 580 | |
| 581 | if (!i2c_fn_i2c && !i2c_fn_block) | ||
| 582 | pdata.page_size = 1; | ||
| 555 | 583 | ||
| 556 | if (!is_power_of_2(chip.byte_len)) | 584 | if (!pdata.page_size) { |
| 557 | dev_warn(&client->dev, | 585 | dev_err(dev, "page_size must not be 0!\n"); |
| 558 | "byte_len looks suspicious (no power of 2)!\n"); | ||
| 559 | if (!chip.page_size) { | ||
| 560 | dev_err(&client->dev, "page_size must not be 0!\n"); | ||
| 561 | return -EINVAL; | 586 | return -EINVAL; |
| 562 | } | 587 | } |
| 563 | if (!is_power_of_2(chip.page_size)) | ||
| 564 | dev_warn(&client->dev, | ||
| 565 | "page_size looks suspicious (no power of 2)!\n"); | ||
| 566 | 588 | ||
| 567 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C) && | 589 | if (!is_power_of_2(pdata.page_size)) |
| 568 | !i2c_check_functionality(client->adapter, | 590 | dev_warn(dev, "page_size looks suspicious (no power of 2)!\n"); |
| 569 | I2C_FUNC_SMBUS_WRITE_I2C_BLOCK)) | ||
| 570 | chip.page_size = 1; | ||
| 571 | 591 | ||
| 572 | if (chip.flags & AT24_FLAG_TAKE8ADDR) | 592 | if (pdata.flags & AT24_FLAG_TAKE8ADDR) |
| 573 | num_addresses = 8; | 593 | num_addresses = 8; |
| 574 | else | 594 | else |
| 575 | num_addresses = DIV_ROUND_UP(chip.byte_len, | 595 | num_addresses = DIV_ROUND_UP(pdata.byte_len, |
| 576 | (chip.flags & AT24_FLAG_ADDR16) ? 65536 : 256); | 596 | (pdata.flags & AT24_FLAG_ADDR16) ? 65536 : 256); |
| 597 | |||
| 598 | if ((pdata.flags & AT24_FLAG_SERIAL) && (pdata.flags & AT24_FLAG_MAC)) { | ||
| 599 | dev_err(dev, | ||
| 600 | "invalid device data - cannot have both AT24_FLAG_SERIAL & AT24_FLAG_MAC."); | ||
| 601 | return -EINVAL; | ||
| 602 | } | ||
| 577 | 603 | ||
| 578 | regmap_config.val_bits = 8; | 604 | regmap_config.val_bits = 8; |
| 579 | regmap_config.reg_bits = (chip.flags & AT24_FLAG_ADDR16) ? 16 : 8; | 605 | regmap_config.reg_bits = (pdata.flags & AT24_FLAG_ADDR16) ? 16 : 8; |
| 606 | regmap_config.disable_locking = true; | ||
| 580 | 607 | ||
| 581 | at24 = devm_kzalloc(&client->dev, sizeof(struct at24_data) + | 608 | regmap = devm_regmap_init_i2c(client, ®map_config); |
| 582 | num_addresses * sizeof(struct at24_client), GFP_KERNEL); | 609 | if (IS_ERR(regmap)) |
| 610 | return PTR_ERR(regmap); | ||
| 611 | |||
| 612 | at24_size = sizeof(*at24) + num_addresses * sizeof(struct at24_client); | ||
| 613 | at24 = devm_kzalloc(dev, at24_size, GFP_KERNEL); | ||
| 583 | if (!at24) | 614 | if (!at24) |
| 584 | return -ENOMEM; | 615 | return -ENOMEM; |
| 585 | 616 | ||
| 586 | mutex_init(&at24->lock); | 617 | mutex_init(&at24->lock); |
| 587 | at24->chip = chip; | 618 | at24->byte_len = pdata.byte_len; |
| 619 | at24->page_size = pdata.page_size; | ||
| 620 | at24->flags = pdata.flags; | ||
| 588 | at24->num_addresses = num_addresses; | 621 | at24->num_addresses = num_addresses; |
| 589 | at24->offset_adj = at24_get_offset_adj(chip.flags, chip.byte_len); | 622 | at24->offset_adj = at24_get_offset_adj(pdata.flags, pdata.byte_len); |
| 623 | at24->client[0].client = client; | ||
| 624 | at24->client[0].regmap = regmap; | ||
| 590 | 625 | ||
| 591 | at24->wp_gpio = devm_gpiod_get_optional(&client->dev, | 626 | at24->wp_gpio = devm_gpiod_get_optional(dev, "wp", GPIOD_OUT_HIGH); |
| 592 | "wp", GPIOD_OUT_HIGH); | ||
| 593 | if (IS_ERR(at24->wp_gpio)) | 627 | if (IS_ERR(at24->wp_gpio)) |
| 594 | return PTR_ERR(at24->wp_gpio); | 628 | return PTR_ERR(at24->wp_gpio); |
| 595 | 629 | ||
| 596 | at24->client[0].client = client; | 630 | writable = !(pdata.flags & AT24_FLAG_READONLY); |
| 597 | at24->client[0].regmap = devm_regmap_init_i2c(client, ®map_config); | ||
| 598 | if (IS_ERR(at24->client[0].regmap)) | ||
| 599 | return PTR_ERR(at24->client[0].regmap); | ||
| 600 | |||
| 601 | if ((chip.flags & AT24_FLAG_SERIAL) && (chip.flags & AT24_FLAG_MAC)) { | ||
| 602 | dev_err(&client->dev, | ||
| 603 | "invalid device data - cannot have both AT24_FLAG_SERIAL & AT24_FLAG_MAC."); | ||
| 604 | return -EINVAL; | ||
| 605 | } | ||
| 606 | |||
| 607 | writable = !(chip.flags & AT24_FLAG_READONLY); | ||
| 608 | if (writable) { | 631 | if (writable) { |
| 609 | at24->write_max = min_t(unsigned int, | 632 | at24->write_max = min_t(unsigned int, |
| 610 | chip.page_size, at24_io_limit); | 633 | pdata.page_size, at24_io_limit); |
| 611 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C) && | 634 | if (!i2c_fn_i2c && at24->write_max > I2C_SMBUS_BLOCK_MAX) |
| 612 | at24->write_max > I2C_SMBUS_BLOCK_MAX) | ||
| 613 | at24->write_max = I2C_SMBUS_BLOCK_MAX; | 635 | at24->write_max = I2C_SMBUS_BLOCK_MAX; |
| 614 | } | 636 | } |
| 615 | 637 | ||
| @@ -618,8 +640,8 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id) · 2 unmodified lines | |||
| 618 | at24->client[i].client = i2c_new_dummy(client->adapter, | 640 | at24->client[i].client = i2c_new_dummy(client->adapter, |
| 619 | client->addr + i); | 641 | client->addr + i); |
| 620 | if (!at24->client[i].client) { | 642 | if (!at24->client[i].client) { |
| 621 | dev_err(&client->dev, "address 0x%02x unavailable\n", | 643 | dev_err(dev, "address 0x%02x unavailable\n", |
| 622 | client->addr + i); | 644 | client->addr + i); |
| 623 | err = -EADDRINUSE; | 645 | err = -EADDRINUSE; |
| 624 | goto err_clients; | 646 | goto err_clients; |
| 625 | } | 647 | } |
| @@ -635,48 +657,47 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id) · 9 unmodified lines | |||
| 635 | i2c_set_clientdata(client, at24); | 657 | i2c_set_clientdata(client, at24); |
| 636 | 658 | ||
| 637 | /* enable runtime pm */ | 659 | /* enable runtime pm */ |
| 638 | pm_runtime_set_active(&client->dev); | 660 | pm_runtime_set_active(dev); |
| 639 | pm_runtime_enable(&client->dev); | 661 | pm_runtime_enable(dev); |
| 640 | 662 | ||
| 641 | /* | 663 | /* |
| 642 | * Perform a one-byte test read to verify that the | 664 | * Perform a one-byte test read to verify that the |
| 643 | * chip is functional. | 665 | * chip is functional. |
| 644 | */ | 666 | */ |
| 645 | err = at24_read(at24, 0, &test_byte, 1); | 667 | err = at24_read(at24, 0, &test_byte, 1); |
| 646 | pm_runtime_idle(&client->dev); | 668 | pm_runtime_idle(dev); |
| 647 | if (err) { | 669 | if (err) { |
| 648 | err = -ENODEV; | 670 | err = -ENODEV; |
| 649 | goto err_clients; | 671 | goto err_clients; |
| 650 | } | 672 | } |
| 651 | 673 | ||
| 652 | at24->nvmem_config.name = dev_name(&client->dev); | 674 | nvmem_config.name = dev_name(dev); |
| 653 | at24->nvmem_config.dev = &client->dev; | 675 | nvmem_config.dev = dev; |
| 654 | at24->nvmem_config.read_only = !writable; | 676 | nvmem_config.read_only = !writable; |
| 655 | at24->nvmem_config.root_only = true; | 677 | nvmem_config.root_only = true; |
| 656 | at24->nvmem_config.owner = THIS_MODULE; | 678 | nvmem_config.owner = THIS_MODULE; |
| 657 | at24->nvmem_config.compat = true; | 679 | nvmem_config.compat = true; |
| 658 | at24->nvmem_config.base_dev = &client->dev; | 680 | nvmem_config.base_dev = dev; |
| 659 | at24->nvmem_config.reg_read = at24_read; | 681 | nvmem_config.reg_read = at24_read; |
| 660 | at24->nvmem_config.reg_write = at24_write; | 682 | nvmem_config.reg_write = at24_write; |
| 661 | at24->nvmem_config.priv = at24; | 683 | nvmem_config.priv = at24; |
| 662 | at24->nvmem_config.stride = 1; | 684 | nvmem_config.stride = 1; |
| 663 | at24->nvmem_config.word_size = 1; | 685 | nvmem_config.word_size = 1; |
| 664 | at24->nvmem_config.size = chip.byte_len; | 686 | nvmem_config.size = pdata.byte_len; |
| 665 | 687 | ||
| 666 | at24->nvmem = nvmem_register(&at24->nvmem_config); | 688 | at24->nvmem = nvmem_register(&nvmem_config); |
| 667 | |||
| 668 | if (IS_ERR(at24->nvmem)) { | 689 | if (IS_ERR(at24->nvmem)) { |
| 669 | err = PTR_ERR(at24->nvmem); | 690 | err = PTR_ERR(at24->nvmem); |
| 670 | goto err_clients; | 691 | goto err_clients; |
| 671 | } | 692 | } |
| 672 | 693 | ||
| 673 | dev_info(&client->dev, "%u byte %s EEPROM, %s, %u bytes/write\n", | 694 | dev_info(dev, "%u byte %s EEPROM, %s, %u bytes/write\n", |
| 674 | chip.byte_len, client->name, | 695 | pdata.byte_len, client->name, |
| 675 | writable ? "writable" : "read-only", at24->write_max); | 696 | writable ? "writable" : "read-only", at24->write_max); |
| 676 | 697 | ||
| 677 | /* export data to kernel code */ | 698 | /* export data to kernel code */ |
| 678 | if (chip.setup) | 699 | if (pdata.setup) |
| 679 | chip.setup(at24->nvmem, chip.context); | 700 | pdata.setup(at24->nvmem, pdata.context); |
| 680 | 701 | ||
| 681 | return 0; | 702 | return 0; |
| 682 | 703 | ||
| @@ -685,7 +706,7 @@ err_clients: · 2 unmodified lines | |||
| 685 | if (at24->client[i].client) | 706 | if (at24->client[i].client) |
| 686 | i2c_unregister_device(at24->client[i].client); | 707 | i2c_unregister_device(at24->client[i].client); |
| 687 | 708 | ||
| 688 | pm_runtime_disable(&client->dev); | 709 | pm_runtime_disable(dev); |
| 689 | 710 | ||
| 690 | return err; | 711 | return err; |
| 691 | } | 712 | } |
| @@ -708,15 +729,13 @@ static int at24_remove(struct i2c_client *client) · 16 unmodified lines | |||
| 708 | return 0; | 729 | return 0; |
| 709 | } | 730 | } |
| 710 | 731 | ||
| 711 | /*-------------------------------------------------------------------------*/ | ||
| 712 | |||
| 713 | static struct i2c_driver at24_driver = { | 732 | static struct i2c_driver at24_driver = { |
| 714 | .driver = { | 733 | .driver = { |
| 715 | .name = "at24", | 734 | .name = "at24", |
| 716 | .of_match_table = at24_of_match, | 735 | .of_match_table = at24_of_match, |
| 717 | .acpi_match_table = ACPI_PTR(at24_acpi_ids), | 736 | .acpi_match_table = ACPI_PTR(at24_acpi_ids), |
| 718 | }, | 737 | }, |
| 719 | .probe = at24_probe, | 738 | .probe_new = at24_probe, |
| 720 | .remove = at24_remove, | 739 | .remove = at24_remove, |
| 721 | .id_table = at24_ids, | 740 | .id_table = at24_ids, |
| 722 | }; | 741 | }; |
| @@ -102,7 +102,7 @@ static int at25_ee_read(void *priv, unsigned int offset, · 101 unmodified lines | |||
| 102 | } | 102 | } |
| 103 | 103 | ||
| 104 | spi_message_init(&m); | 104 | spi_message_init(&m); |
| 105 | memset(t, 0, sizeof t); | 105 | memset(t, 0, sizeof(t)); |
| 106 | 106 | ||
| 107 | t[0].tx_buf = command; | 107 | t[0].tx_buf = command; |
| 108 | t[0].len = at25->addrlen + 1; | 108 | t[0].len = at25->addrlen + 1; |
| @@ -0,0 +1,20 @@ | |||
| 1 | # SPDX-License-Identifier: GPL-2.0 | ||
| 2 | obj-$(CONFIG_LKDTM) += lkdtm.o | ||
| 3 | |||
| 4 | lkdtm-$(CONFIG_LKDTM) += core.o | ||
| 5 | lkdtm-$(CONFIG_LKDTM) += bugs.o | ||
| 6 | lkdtm-$(CONFIG_LKDTM) += heap.o | ||
| 7 | lkdtm-$(CONFIG_LKDTM) += perms.o | ||
| 8 | lkdtm-$(CONFIG_LKDTM) += refcount.o | ||
| 9 | lkdtm-$(CONFIG_LKDTM) += rodata_objcopy.o | ||
| 10 | lkdtm-$(CONFIG_LKDTM) += usercopy.o | ||
| 11 | |||
| 12 | KCOV_INSTRUMENT_rodata.o := n | ||
| 13 | |||
| 14 | OBJCOPYFLAGS := | ||
| 15 | OBJCOPYFLAGS_rodata_objcopy.o := \ | ||
| 16 | --set-section-flags .text=alloc,readonly \ | ||
| 17 | --rename-section .text=.rodata | ||
| 18 | targets += rodata.o rodata_objcopy.o | ||
| 19 | $(obj)/rodata_objcopy.o: $(obj)/rodata.o FORCE | ||
| 20 | $(call if_changed,objcopy) | ||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
| @@ -1,3 +1,4 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 1 | /* | 2 | /* |
| 2 | * This is for all the tests related to refcount bugs (e.g. overflow, | 3 | * This is for all the tests related to refcount bugs (e.g. overflow, |
| 3 | * underflow, reaching zero untested, etc). | 4 | * underflow, reaching zero untested, etc). |
File renamed without changes.
File renamed without changes.
| @@ -74,6 +74,23 @@ ssize_t __mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length, · 73 unmodified lines | |||
| 74 | goto out; | 74 | goto out; |
| 75 | } | 75 | } |
| 76 | 76 | ||
| 77 | while (cl->tx_cb_queued >= bus->tx_queue_limit) { | ||
| 78 | mutex_unlock(&bus->device_lock); | ||
| 79 | rets = wait_event_interruptible(cl->tx_wait, | ||
| 80 | cl->writing_state == MEI_WRITE_COMPLETE || | ||
| 81 | (!mei_cl_is_connected(cl))); | ||
| 82 | mutex_lock(&bus->device_lock); | ||
| 83 | if (rets) { | ||
| 84 | if (signal_pending(current)) | ||
| 85 | rets = -EINTR; | ||
| 86 | goto out; | ||
| 87 | } | ||
| 88 | if (!mei_cl_is_connected(cl)) { | ||
| 89 | rets = -ENODEV; | ||
| 90 | goto out; | ||
| 91 | } | ||
| 92 | } | ||
| 93 | |||
| 77 | cb = mei_cl_alloc_cb(cl, length, MEI_FOP_WRITE, NULL); | 94 | cb = mei_cl_alloc_cb(cl, length, MEI_FOP_WRITE, NULL); |
| 78 | if (!cb) { | 95 | if (!cb) { |
| 79 | rets = -ENOMEM; | 96 | rets = -ENOMEM; |
| @@ -449,6 +466,29 @@ bool mei_cldev_enabled(struct mei_cl_device *cldev) · 369 unmodified lines | |||
| 449 | } | 466 | } |
| 450 | EXPORT_SYMBOL_GPL(mei_cldev_enabled); | 467 | EXPORT_SYMBOL_GPL(mei_cldev_enabled); |
| 451 | 468 | ||
| 469 | /** | ||
| 470 | * mei_cl_bus_module_get - acquire module of the underlying | ||
| 471 | * hw driver. | ||
| 472 | * | ||
| 473 | * @cldev: mei client device | ||
| 474 | * | ||
| 475 | * Return: true on success; false if the module was removed. | ||
| 476 | */ | ||
| 477 | static bool mei_cl_bus_module_get(struct mei_cl_device *cldev) | ||
| 478 | { | ||
| 479 | return try_module_get(cldev->bus->dev->driver->owner); | ||
| 480 | } | ||
| 481 | |||
| 482 | /** | ||
| 483 | * mei_cl_bus_module_put - release the underlying hw module. | ||
| 484 | * | ||
| 485 | * @cldev: mei client device | ||
| 486 | */ | ||
| 487 | static void mei_cl_bus_module_put(struct mei_cl_device *cldev) | ||
| 488 | { | ||
| 489 | module_put(cldev->bus->dev->driver->owner); | ||
| 490 | } | ||
| 491 | |||
| 452 | /** | 492 | /** |
| 453 | * mei_cldev_enable - enable me client device | 493 | * mei_cldev_enable - enable me client device |
| 454 | * create connection with me client | 494 | * create connection with me client |
| @@ -487,9 +527,17 @@ int mei_cldev_enable(struct mei_cl_device *cldev) · 32 unmodified lines | |||
| 487 | goto out; | 527 | goto out; |
| 488 | } | 528 | } |
| 489 | 529 | ||
| 530 | if (!mei_cl_bus_module_get(cldev)) { | ||
| 531 | dev_err(&cldev->dev, "get hw module failed"); | ||
| 532 | ret = -ENODEV; | ||
| 533 | goto out; | ||
| 534 | } | ||
| 535 | |||
| 490 | ret = mei_cl_connect(cl, cldev->me_cl, NULL); | 536 | ret = mei_cl_connect(cl, cldev->me_cl, NULL); |
| 491 | if (ret < 0) | 537 | if (ret < 0) { |
| 492 | dev_err(&cldev->dev, "cannot connect\n"); | 538 | dev_err(&cldev->dev, "cannot connect\n"); |
| 539 | mei_cl_bus_module_put(cldev); | ||
| 540 | } | ||
| 493 | 541 | ||
| 494 | out: | 542 | out: |
| 495 | mutex_unlock(&bus->device_lock); | 543 | mutex_unlock(&bus->device_lock); |
| @@ -553,6 +601,8 @@ int mei_cldev_disable(struct mei_cl_device *cldev) · 57 unmodified lines | |||
| 553 | dev_err(bus->dev, "Could not disconnect from the ME client\n"); | 601 | dev_err(bus->dev, "Could not disconnect from the ME client\n"); |
| 554 | 602 | ||
| 555 | out: | 603 | out: |
| 604 | mei_cl_bus_module_put(cldev); | ||
| 605 | |||
| 556 | /* Flush queues and remove any pending read */ | 606 | /* Flush queues and remove any pending read */ |
| 557 | mei_cl_flush_queues(cl, NULL); | 607 | mei_cl_flush_queues(cl, NULL); |
| 558 | mei_cl_unlink(cl); | 608 | mei_cl_unlink(cl); |
| @@ -562,37 +612,6 @@ out: · 3 unmodified lines | |||
| 562 | } | 612 | } |
| 563 | EXPORT_SYMBOL_GPL(mei_cldev_disable); | 613 | EXPORT_SYMBOL_GPL(mei_cldev_disable); |
| 564 | 614 | ||
| 565 | /** | ||
| 566 | * mei_cl_bus_module_get - acquire module of the underlying | ||
| 567 | * hw module. | ||
| 568 | * | ||
| 569 | * @cl: host client | ||
| 570 | * | ||
| 571 | * Return: true on success; false if the module was removed. | ||
| 572 | */ | ||
| 573 | bool mei_cl_bus_module_get(struct mei_cl *cl) | ||
| 574 | { | ||
| 575 | struct mei_cl_device *cldev = cl->cldev; | ||
| 576 | |||
| 577 | if (!cldev) | ||
| 578 | return true; | ||
| 579 | |||
| 580 | return try_module_get(cldev->bus->dev->driver->owner); | ||
| 581 | } | ||
| 582 | |||
| 583 | /** | ||
| 584 | * mei_cl_bus_module_put - release the underlying hw module. | ||
| 585 | * | ||
| 586 | * @cl: host client | ||
| 587 | */ | ||
| 588 | void mei_cl_bus_module_put(struct mei_cl *cl) | ||
| 589 | { | ||
| 590 | struct mei_cl_device *cldev = cl->cldev; | ||
| 591 | |||
| 592 | if (cldev) | ||
| 593 | module_put(cldev->bus->dev->driver->owner); | ||
| 594 | } | ||
| 595 | |||
| 596 | /** | 615 | /** |
| 597 | * mei_cl_device_find - find matching entry in the driver id table | 616 | * mei_cl_device_find - find matching entry in the driver id table |
| 598 | * | 617 | * |
| @@ -349,6 +349,36 @@ void mei_io_cb_free(struct mei_cl_cb *cb) · 348 unmodified lines | |||
| 349 | kfree(cb); | 349 | kfree(cb); |
| 350 | } | 350 | } |
| 351 | 351 | ||
| 352 | /** | ||
| 353 | * mei_tx_cb_queue - queue tx callback | ||
| 354 | * | ||
| 355 | * Locking: called under "dev->device_lock" lock | ||
| 356 | * | ||
| 357 | * @cb: mei callback struct | ||
| 358 | * @head: an instance of list to queue on | ||
| 359 | */ | ||
| 360 | static inline void mei_tx_cb_enqueue(struct mei_cl_cb *cb, | ||
| 361 | struct list_head *head) | ||
| 362 | { | ||
| 363 | list_add_tail(&cb->list, head); | ||
| 364 | cb->cl->tx_cb_queued++; | ||
| 365 | } | ||
| 366 | |||
| 367 | /** | ||
| 368 | * mei_tx_cb_dequeue - dequeue tx callback | ||
| 369 | * | ||
| 370 | * Locking: called under "dev->device_lock" lock | ||
| 371 | * | ||
| 372 | * @cb: mei callback struct to dequeue and free | ||
| 373 | */ | ||
| 374 | static inline void mei_tx_cb_dequeue(struct mei_cl_cb *cb) | ||
| 375 | { | ||
| 376 | if (!WARN_ON(cb->cl->tx_cb_queued == 0)) | ||
| 377 | cb->cl->tx_cb_queued--; | ||
| 378 | |||
| 379 | mei_io_cb_free(cb); | ||
| 380 | } | ||
| 381 | |||
| 352 | /** | 382 | /** |
| 353 | * mei_io_cb_init - allocate and initialize io callback | 383 | * mei_io_cb_init - allocate and initialize io callback |
| 354 | * | 384 | * |
| @@ -377,49 +407,37 @@ static struct mei_cl_cb *mei_io_cb_init(struct mei_cl *cl, · 22 unmodified lines | |||
| 377 | } | 407 | } |
| 378 | 408 | ||
| 379 | /** | 409 | /** |
| 380 | * __mei_io_list_flush_cl - removes and frees cbs belonging to cl. | 410 | * mei_io_list_flush_cl - removes cbs belonging to the cl. |
| 381 | * | 411 | * |
| 382 | * @head: an instance of our list structure | 412 | * @head: an instance of our list structure |
| 383 | * @cl: host client, can be NULL for flushing the whole list | 413 | * @cl: host client |
| 384 | * @free: whether to free the cbs | ||
| 385 | */ | 414 | */ |
| 386 | static void __mei_io_list_flush_cl(struct list_head *head, | 415 | static void mei_io_list_flush_cl(struct list_head *head, |
| 387 | const struct mei_cl *cl, bool free) | 416 | const struct mei_cl *cl) |
| 388 | { | 417 | { |
| 389 | struct mei_cl_cb *cb, *next; | 418 | struct mei_cl_cb *cb, *next; |
| 390 | 419 | ||
| 391 | /* enable removing everything if no cl is specified */ | ||
| 392 | list_for_each_entry_safe(cb, next, head, list) { | 420 | list_for_each_entry_safe(cb, next, head, list) { |
| 393 | if (!cl || mei_cl_cmp_id(cl, cb->cl)) { | 421 | if (mei_cl_cmp_id(cl, cb->cl)) |
| 394 | list_del_init(&cb->list); | 422 | list_del_init(&cb->list); |
| 395 | if (free) | ||
| 396 | mei_io_cb_free(cb); | ||
| 397 | } | ||
| 398 | } | 423 | } |
| 399 | } | 424 | } |
| 400 | 425 | ||
| 401 | /** | 426 | /** |
| 402 | * mei_io_list_flush_cl - removes list entry belonging to cl. | 427 | * mei_io_tx_list_free_cl - removes cb belonging to the cl and free them |
| 403 | * | 428 | * |
| 404 | * @head: An instance of our list structure | 429 | * @head: An instance of our list structure |
| 405 | * @cl: host client | 430 | * @cl: host client |
| 406 | */ | 431 | */ |
| 407 | static inline void mei_io_list_flush_cl(struct list_head *head, | 432 | static void mei_io_tx_list_free_cl(struct list_head *head, |
| 408 | const struct mei_cl *cl) | 433 | const struct mei_cl *cl) |
| 409 | { | 434 | { |
| 410 | __mei_io_list_flush_cl(head, cl, false); | 435 | struct mei_cl_cb *cb, *next; |
| 411 | } | ||
| 412 | 436 | ||
| 413 | /** | 437 | list_for_each_entry_safe(cb, next, head, list) { |
| 414 | * mei_io_list_free_cl - removes cb belonging to cl and free them | 438 | if (mei_cl_cmp_id(cl, cb->cl)) |
| 415 | * | 439 | mei_tx_cb_dequeue(cb); |
| 416 | * @head: An instance of our list structure | 440 | } |
| 417 | * @cl: host client | ||
| 418 | */ | ||
| 419 | static inline void mei_io_list_free_cl(struct list_head *head, | ||
| 420 | const struct mei_cl *cl) | ||
| 421 | { | ||
| 422 | __mei_io_list_flush_cl(head, cl, true); | ||
| 423 | } | 441 | } |
| 424 | 442 | ||
| 425 | /** | 443 | /** |
| @@ -538,8 +556,8 @@ int mei_cl_flush_queues(struct mei_cl *cl, const struct file *fp) · 112 unmodified lines | |||
| 538 | dev = cl->dev; | 556 | dev = cl->dev; |
| 539 | 557 | ||
| 540 | cl_dbg(dev, cl, "remove list entry belonging to cl\n"); | 558 | cl_dbg(dev, cl, "remove list entry belonging to cl\n"); |
| 541 | mei_io_list_free_cl(&cl->dev->write_list, cl); | 559 | mei_io_tx_list_free_cl(&cl->dev->write_list, cl); |
| 542 | mei_io_list_free_cl(&cl->dev->write_waiting_list, cl); | 560 | mei_io_tx_list_free_cl(&cl->dev->write_waiting_list, cl); |
| 543 | mei_io_list_flush_cl(&cl->dev->ctrl_wr_list, cl); | 561 | mei_io_list_flush_cl(&cl->dev->ctrl_wr_list, cl); |
| 544 | mei_io_list_flush_cl(&cl->dev->ctrl_rd_list, cl); | 562 | mei_io_list_flush_cl(&cl->dev->ctrl_rd_list, cl); |
| 545 | mei_io_list_free_fp(&cl->rd_pending, fp); | 563 | mei_io_list_free_fp(&cl->rd_pending, fp); |
| @@ -756,8 +774,8 @@ static void mei_cl_set_disconnected(struct mei_cl *cl) · 210 unmodified lines | |||
| 756 | return; | 774 | return; |
| 757 | 775 | ||
| 758 | cl->state = MEI_FILE_DISCONNECTED; | 776 | cl->state = MEI_FILE_DISCONNECTED; |
| 759 | mei_io_list_free_cl(&dev->write_list, cl); | 777 | mei_io_tx_list_free_cl(&dev->write_list, cl); |
| 760 | mei_io_list_free_cl(&dev->write_waiting_list, cl); | 778 | mei_io_tx_list_free_cl(&dev->write_waiting_list, cl); |
| 761 | mei_io_list_flush_cl(&dev->ctrl_rd_list, cl); | 779 | mei_io_list_flush_cl(&dev->ctrl_rd_list, cl); |
| 762 | mei_io_list_flush_cl(&dev->ctrl_wr_list, cl); | 780 | mei_io_list_flush_cl(&dev->ctrl_wr_list, cl); |
| 763 | mei_cl_wake_all(cl); | 781 | mei_cl_wake_all(cl); |
| @@ -765,8 +783,6 @@ static void mei_cl_set_disconnected(struct mei_cl *cl) · 1 unmodified line | |||
| 765 | cl->tx_flow_ctrl_creds = 0; | 783 | cl->tx_flow_ctrl_creds = 0; |
| 766 | cl->timer_count = 0; | 784 | cl->timer_count = 0; |
| 767 | 785 | ||
| 768 | mei_cl_bus_module_put(cl); | ||
| 769 | |||
| 770 | if (!cl->me_cl) | 786 | if (!cl->me_cl) |
| 771 | return; | 787 | return; |
| 772 | 788 | ||
| @@ -1076,9 +1092,6 @@ int mei_cl_connect(struct mei_cl *cl, struct mei_me_client *me_cl, · 303 unmodified lines | |||
| 1076 | 1092 | ||
| 1077 | dev = cl->dev; | 1093 | dev = cl->dev; |
| 1078 | 1094 | ||
| 1079 | if (!mei_cl_bus_module_get(cl)) | ||
| 1080 | return -ENODEV; | ||
| 1081 | |||
| 1082 | rets = mei_cl_set_connecting(cl, me_cl); | 1095 | rets = mei_cl_set_connecting(cl, me_cl); |
| 1083 | if (rets) | 1096 | if (rets) |
| 1084 | goto nortpm; | 1097 | goto nortpm; |
| @@ -1698,9 +1711,9 @@ int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb) · 613 unmodified lines | |||
| 1698 | 1711 | ||
| 1699 | out: | 1712 | out: |
| 1700 | if (mei_hdr.msg_complete) | 1713 | if (mei_hdr.msg_complete) |
| 1701 | list_add_tail(&cb->list, &dev->write_waiting_list); | 1714 | mei_tx_cb_enqueue(cb, &dev->write_waiting_list); |
| 1702 | else | 1715 | else |
| 1703 | list_add_tail(&cb->list, &dev->write_list); | 1716 | mei_tx_cb_enqueue(cb, &dev->write_list); |
| 1704 | 1717 | ||
| 1705 | cb = NULL; | 1718 | cb = NULL; |
| 1706 | if (blocking && cl->writing_state != MEI_WRITE_COMPLETE) { | 1719 | if (blocking && cl->writing_state != MEI_WRITE_COMPLETE) { |
| @@ -1746,7 +1759,7 @@ void mei_cl_complete(struct mei_cl *cl, struct mei_cl_cb *cb) · 39 unmodified lines | |||
| 1746 | 1759 | ||
| 1747 | switch (cb->fop_type) { | 1760 | switch (cb->fop_type) { |
| 1748 | case MEI_FOP_WRITE: | 1761 | case MEI_FOP_WRITE: |
| 1749 | mei_io_cb_free(cb); | 1762 | mei_tx_cb_dequeue(cb); |
| 1750 | cl->writing_state = MEI_WRITE_COMPLETE; | 1763 | cl->writing_state = MEI_WRITE_COMPLETE; |
| 1751 | if (waitqueue_active(&cl->tx_wait)) { | 1764 | if (waitqueue_active(&cl->tx_wait)) { |
| 1752 | wake_up_interruptible(&cl->tx_wait); | 1765 | wake_up_interruptible(&cl->tx_wait); |
| @@ -97,7 +97,7 @@ static ssize_t mei_dbgfs_read_active(struct file *fp, char __user *ubuf, · 96 unmodified lines | |||
| 97 | int pos = 0; | 97 | int pos = 0; |
| 98 | int ret; | 98 | int ret; |
| 99 | 99 | ||
| 100 | #define HDR " |me|host|state|rd|wr|\n" | 100 | #define HDR " |me|host|state|rd|wr|wrq\n" |
| 101 | 101 | ||
| 102 | if (!dev) | 102 | if (!dev) |
| 103 | return -ENODEV; | 103 | return -ENODEV; |
| @@ -130,9 +130,10 @@ static ssize_t mei_dbgfs_read_active(struct file *fp, char __user *ubuf, · 26 unmodified lines | |||
| 130 | list_for_each_entry(cl, &dev->file_list, link) { | 130 | list_for_each_entry(cl, &dev->file_list, link) { |
| 131 | 131 | ||
| 132 | pos += scnprintf(buf + pos, bufsz - pos, | 132 | pos += scnprintf(buf + pos, bufsz - pos, |
| 133 | "%3d|%2d|%4d|%5d|%2d|%2d|\n", | 133 | "%3d|%2d|%4d|%5d|%2d|%2d|%3u\n", |
| 134 | i, mei_cl_me_id(cl), cl->host_client_id, cl->state, | 134 | i, mei_cl_me_id(cl), cl->host_client_id, cl->state, |
| 135 | !list_empty(&cl->rd_completed), cl->writing_state); | 135 | !list_empty(&cl->rd_completed), cl->writing_state, |
| 136 | cl->tx_cb_queued); | ||
| 136 | i++; | 137 | i++; |
| 137 | } | 138 | } |
| 138 | out: | 139 | out: |
| @@ -383,6 +383,7 @@ void mei_device_init(struct mei_device *dev, · 382 unmodified lines | |||
| 383 | INIT_LIST_HEAD(&dev->write_waiting_list); | 383 | INIT_LIST_HEAD(&dev->write_waiting_list); |
| 384 | INIT_LIST_HEAD(&dev->ctrl_wr_list); | 384 | INIT_LIST_HEAD(&dev->ctrl_wr_list); |
| 385 | INIT_LIST_HEAD(&dev->ctrl_rd_list); | 385 | INIT_LIST_HEAD(&dev->ctrl_rd_list); |
| 386 | dev->tx_queue_limit = MEI_TX_QUEUE_LIMIT_DEFAULT; | ||
| 386 | 387 | ||
| 387 | INIT_DELAYED_WORK(&dev->timer_work, mei_timer); | 388 | INIT_DELAYED_WORK(&dev->timer_work, mei_timer); |
| 388 | INIT_WORK(&dev->reset_work, mei_reset_work); | 389 | INIT_WORK(&dev->reset_work, mei_reset_work); |
| @@ -291,6 +291,27 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf, · 290 unmodified lines | |||
| 291 | goto out; | 291 | goto out; |
| 292 | } | 292 | } |
| 293 | 293 | ||
| 294 | while (cl->tx_cb_queued >= dev->tx_queue_limit) { | ||
| 295 | if (file->f_flags & O_NONBLOCK) { | ||
| 296 | rets = -EAGAIN; | ||
| 297 | goto out; | ||
| 298 | } | ||
| 299 | mutex_unlock(&dev->device_lock); | ||
| 300 | rets = wait_event_interruptible(cl->tx_wait, | ||
| 301 | cl->writing_state == MEI_WRITE_COMPLETE || | ||
| 302 | (!mei_cl_is_connected(cl))); | ||
| 303 | mutex_lock(&dev->device_lock); | ||
| 304 | if (rets) { | ||
| 305 | if (signal_pending(current)) | ||
| 306 | rets = -EINTR; | ||
| 307 | goto out; | ||
| 308 | } | ||
| 309 | if (!mei_cl_is_connected(cl)) { | ||
| 310 | rets = -ENODEV; | ||
| 311 | goto out; | ||
| 312 | } | ||
| 313 | } | ||
| 314 | |||
| 294 | *offset = 0; | 315 | *offset = 0; |
| 295 | cb = mei_cl_alloc_cb(cl, length, MEI_FOP_WRITE, file); | 316 | cb = mei_cl_alloc_cb(cl, length, MEI_FOP_WRITE, file); |
| 296 | if (!cb) { | 317 | if (!cb) { |
| @@ -507,7 +528,6 @@ static long mei_ioctl(struct file *file, unsigned int cmd, unsigned long data) · 210 unmodified lines | |||
| 507 | break; | 528 | break; |
| 508 | 529 | ||
| 509 | default: | 530 | default: |
| 510 | dev_err(dev->dev, ": unsupported ioctl %d.\n", cmd); | ||
| 511 | rets = -ENOIOCTLCMD; | 531 | rets = -ENOIOCTLCMD; |
| 512 | } | 532 | } |
| 513 | 533 | ||
| @@ -580,6 +600,12 @@ static __poll_t mei_poll(struct file *file, poll_table *wait) · 66 unmodified lines | |||
| 580 | mei_cl_read_start(cl, mei_cl_mtu(cl), file); | 600 | mei_cl_read_start(cl, mei_cl_mtu(cl), file); |
| 581 | } | 601 | } |
| 582 | 602 | ||
| 603 | if (req_events & (POLLOUT | POLLWRNORM)) { | ||
| 604 | poll_wait(file, &cl->tx_wait, wait); | ||
| 605 | if (cl->tx_cb_queued < dev->tx_queue_limit) | ||
| 606 | mask |= POLLOUT | POLLWRNORM; | ||
| 607 | } | ||
| 608 | |||
| 583 | out: | 609 | out: |
| 584 | mutex_unlock(&dev->device_lock); | 610 | mutex_unlock(&dev->device_lock); |
| 585 | return mask; | 611 | return mask; |
| @@ -749,10 +775,48 @@ static ssize_t hbm_ver_drv_show(struct device *device, · 163 unmodified lines | |||
| 749 | } | 775 | } |
| 750 | static DEVICE_ATTR_RO(hbm_ver_drv); | 776 | static DEVICE_ATTR_RO(hbm_ver_drv); |
| 751 | 777 | ||
| 778 | static ssize_t tx_queue_limit_show(struct device *device, | ||
| 779 | struct device_attribute *attr, char *buf) | ||
| 780 | { | ||
| 781 | struct mei_device *dev = dev_get_drvdata(device); | ||
| 782 | u8 size = 0; | ||
| 783 | |||
| 784 | mutex_lock(&dev->device_lock); | ||
| 785 | size = dev->tx_queue_limit; | ||
| 786 | mutex_unlock(&dev->device_lock); | ||
| 787 | |||
| 788 | return snprintf(buf, PAGE_SIZE, "%u\n", size); | ||
| 789 | } | ||
| 790 | |||
| 791 | static ssize_t tx_queue_limit_store(struct device *device, | ||
| 792 | struct device_attribute *attr, | ||
| 793 | const char *buf, size_t count) | ||
| 794 | { | ||
| 795 | struct mei_device *dev = dev_get_drvdata(device); | ||
| 796 | u8 limit; | ||
| 797 | unsigned int inp; | ||
| 798 | int err; | ||
| 799 | |||
| 800 | err = kstrtouint(buf, 10, &inp); | ||
| 801 | if (err) | ||
| 802 | return err; | ||
| 803 | if (inp > MEI_TX_QUEUE_LIMIT_MAX || inp < MEI_TX_QUEUE_LIMIT_MIN) | ||
| 804 | return -EINVAL; | ||
| 805 | limit = inp; | ||
| 806 | |||
| 807 | mutex_lock(&dev->device_lock); | ||
| 808 | dev->tx_queue_limit = limit; | ||
| 809 | mutex_unlock(&dev->device_lock); | ||
| 810 | |||
| 811 | return count; | ||
| 812 | } | ||
| 813 | static DEVICE_ATTR_RW(tx_queue_limit); | ||
| 814 | |||
| 752 | static struct attribute *mei_attrs[] = { | 815 | static struct attribute *mei_attrs[] = { |
| 753 | &dev_attr_fw_status.attr, | 816 | &dev_attr_fw_status.attr, |
| 754 | &dev_attr_hbm_ver.attr, | 817 | &dev_attr_hbm_ver.attr, |
| 755 | &dev_attr_hbm_ver_drv.attr, | 818 | &dev_attr_hbm_ver_drv.attr, |
| 819 | &dev_attr_tx_queue_limit.attr, | ||
| 756 | NULL | 820 | NULL |
| 757 | }; | 821 | }; |
| 758 | ATTRIBUTE_GROUPS(mei); | 822 | ATTRIBUTE_GROUPS(mei); |
| @@ -210,6 +210,7 @@ struct mei_cl_cb { · 209 unmodified lines | |||
| 210 | * @timer_count: watchdog timer for operation completion | 210 | * @timer_count: watchdog timer for operation completion |
| 211 | * @notify_en: notification - enabled/disabled | 211 | * @notify_en: notification - enabled/disabled |
| 212 | * @notify_ev: pending notification event | 212 | * @notify_ev: pending notification event |
| 213 | * @tx_cb_queued: number of tx callbacks in queue | ||
| 213 | * @writing_state: state of the tx | 214 | * @writing_state: state of the tx |
| 214 | * @rd_pending: pending read credits | 215 | * @rd_pending: pending read credits |
| 215 | * @rd_completed: completed read | 216 | * @rd_completed: completed read |
| @@ -234,6 +235,7 @@ struct mei_cl { · 18 unmodified lines | |||
| 234 | u8 timer_count; | 235 | u8 timer_count; |
| 235 | u8 notify_en; | 236 | u8 notify_en; |
| 236 | u8 notify_ev; | 237 | u8 notify_ev; |
| 238 | u8 tx_cb_queued; | ||
| 237 | enum mei_file_transaction_states writing_state; | 239 | enum mei_file_transaction_states writing_state; |
| 238 | struct list_head rd_pending; | 240 | struct list_head rd_pending; |
| 239 | struct list_head rd_completed; | 241 | struct list_head rd_completed; |
| @@ -241,6 +243,10 @@ struct mei_cl { · 1 unmodified line | |||
| 241 | struct mei_cl_device *cldev; | 243 | struct mei_cl_device *cldev; |
| 242 | }; | 244 | }; |
| 243 | 245 | ||
| 246 | #define MEI_TX_QUEUE_LIMIT_DEFAULT 50 | ||
| 247 | #define MEI_TX_QUEUE_LIMIT_MAX 255 | ||
| 248 | #define MEI_TX_QUEUE_LIMIT_MIN 30 | ||
| 249 | |||
| 244 | /** | 250 | /** |
| 245 | * struct mei_hw_ops - hw specific ops | 251 | * struct mei_hw_ops - hw specific ops |
| 246 | * | 252 | * |
| @@ -315,8 +321,6 @@ ssize_t __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length, · 68 unmodified lines | |||
| 315 | bool mei_cl_bus_rx_event(struct mei_cl *cl); | 321 | bool mei_cl_bus_rx_event(struct mei_cl *cl); |
| 316 | bool mei_cl_bus_notify_event(struct mei_cl *cl); | 322 | bool mei_cl_bus_notify_event(struct mei_cl *cl); |
| 317 | void mei_cl_bus_remove_devices(struct mei_device *bus); | 323 | void mei_cl_bus_remove_devices(struct mei_device *bus); |
| 318 | bool mei_cl_bus_module_get(struct mei_cl *cl); | ||
| 319 | void mei_cl_bus_module_put(struct mei_cl *cl); | ||
| 320 | int mei_cl_bus_init(void); | 324 | int mei_cl_bus_init(void); |
| 321 | void mei_cl_bus_exit(void); | 325 | void mei_cl_bus_exit(void); |
| 322 | 326 | ||
| @@ -361,6 +365,7 @@ const char *mei_pg_state_str(enum mei_pg_state state); · 38 unmodified lines | |||
| 361 | * @write_waiting_list : write completion list | 365 | * @write_waiting_list : write completion list |
| 362 | * @ctrl_wr_list : pending control write list | 366 | * @ctrl_wr_list : pending control write list |
| 363 | * @ctrl_rd_list : pending control read list | 367 | * @ctrl_rd_list : pending control read list |
| 368 | * @tx_queue_limit: tx queues per client linit | ||
| 364 | * | 369 | * |
| 365 | * @file_list : list of opened handles | 370 | * @file_list : list of opened handles |
| 366 | * @open_handle_count: number of opened handles | 371 | * @open_handle_count: number of opened handles |
| @@ -425,6 +430,7 @@ struct mei_device { · 58 unmodified lines | |||
| 425 | struct list_head write_waiting_list; | 430 | struct list_head write_waiting_list; |
| 426 | struct list_head ctrl_wr_list; | 431 | struct list_head ctrl_wr_list; |
| 427 | struct list_head ctrl_rd_list; | 432 | struct list_head ctrl_rd_list; |
| 433 | u8 tx_queue_limit; | ||
| 428 | 434 | ||
| 429 | struct list_head file_list; | 435 | struct list_head file_list; |
| 430 | long open_handle_count; | 436 | long open_handle_count; |
| @@ -135,7 +135,9 @@ EXPORT_SYMBOL_GPL(vop_unregister_driver); · 134 unmodified lines | |||
| 135 | 135 | ||
| 136 | static void vop_release_dev(struct device *d) | 136 | static void vop_release_dev(struct device *d) |
| 137 | { | 137 | { |
| 138 | put_device(d); | 138 | struct vop_device *dev = dev_to_vop(d); |
| 139 | |||
| 140 | kfree(dev); | ||
| 139 | } | 141 | } |
| 140 | 142 | ||
| 141 | struct vop_device * | 143 | struct vop_device * |
| @@ -174,7 +176,7 @@ vop_register_device(struct device *pdev, int id, · 32 unmodified lines | |||
| 174 | goto free_vdev; | 176 | goto free_vdev; |
| 175 | return vdev; | 177 | return vdev; |
| 176 | free_vdev: | 178 | free_vdev: |
| 177 | kfree(vdev); | 179 | put_device(&vdev->dev); |
| 178 | return ERR_PTR(ret); | 180 | return ERR_PTR(ret); |
| 179 | } | 181 | } |
| 180 | EXPORT_SYMBOL_GPL(vop_register_device); | 182 | EXPORT_SYMBOL_GPL(vop_register_device); |
| @@ -519,7 +519,7 @@ static struct ocxl_fn *init_function(struct pci_dev *dev) · 518 unmodified lines | |||
| 519 | rc = device_register(&fn->dev); | 519 | rc = device_register(&fn->dev); |
| 520 | if (rc) { | 520 | if (rc) { |
| 521 | deconfigure_function(fn); | 521 | deconfigure_function(fn); |
| 522 | device_unregister(&fn->dev); | 522 | put_device(&fn->dev); |
| 523 | return ERR_PTR(rc); | 523 | return ERR_PTR(rc); |
| 524 | } | 524 | } |
| 525 | return fn; | 525 | return fn; |
| @@ -167,10 +167,10 @@ config MESON_MX_EFUSE · 166 unmodified lines | |||
| 167 | 167 | ||
| 168 | config NVMEM_SNVS_LPGPR | 168 | config NVMEM_SNVS_LPGPR |
| 169 | tristate "Support for Low Power General Purpose Register" | 169 | tristate "Support for Low Power General Purpose Register" |
| 170 | depends on SOC_IMX6 || COMPILE_TEST | 170 | depends on SOC_IMX6 || SOC_IMX7D || COMPILE_TEST |
| 171 | help | 171 | help |
| 172 | This is a driver for Low Power General Purpose Register (LPGPR) available on | 172 | This is a driver for Low Power General Purpose Register (LPGPR) available on |
| 173 | i.MX6 SoCs in Secure Non-Volatile Storage (SNVS) of this chip. | 173 | i.MX6 and i.MX7 SoCs in Secure Non-Volatile Storage (SNVS) of this chip. |
| 174 | 174 | ||
| 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. |
| @@ -262,8 +262,7 @@ static int bcm_otpc_probe(struct platform_device *pdev) · 261 unmodified lines | |||
| 262 | else if (of_device_is_compatible(dev->of_node, "brcm,ocotp-v2")) | 262 | else if (of_device_is_compatible(dev->of_node, "brcm,ocotp-v2")) |
| 263 | priv->map = &otp_map_v2; | 263 | priv->map = &otp_map_v2; |
| 264 | else { | 264 | else { |
| 265 | dev_err(&pdev->dev, | 265 | dev_err(dev, "%s otpc config map not defined\n", __func__); |
| 266 | "%s otpc config map not defined\n", __func__); | ||
| 267 | return -EINVAL; | 266 | return -EINVAL; |
| 268 | } | 267 | } |
| 269 | 268 | ||
| @@ -302,27 +301,17 @@ static int bcm_otpc_probe(struct platform_device *pdev) · 32 unmodified lines | |||
| 302 | 301 | ||
| 303 | priv->config = &bcm_otpc_nvmem_config; | 302 | priv->config = &bcm_otpc_nvmem_config; |
| 304 | 303 | ||
| 305 | nvmem = nvmem_register(&bcm_otpc_nvmem_config); | 304 | nvmem = devm_nvmem_register(dev, &bcm_otpc_nvmem_config); |
| 306 | if (IS_ERR(nvmem)) { | 305 | if (IS_ERR(nvmem)) { |
| 307 | dev_err(dev, "error registering nvmem config\n"); | 306 | dev_err(dev, "error registering nvmem config\n"); |
| 308 | return PTR_ERR(nvmem); | 307 | return PTR_ERR(nvmem); |
| 309 | } | 308 | } |
| 310 | 309 | ||
| 311 | platform_set_drvdata(pdev, nvmem); | ||
| 312 | |||
| 313 | return 0; | 310 | return 0; |
| 314 | } | 311 | } |
| 315 | 312 | ||
| 316 | static int bcm_otpc_remove(struct platform_device *pdev) | ||
| 317 | { | ||
| 318 | struct nvmem_device *nvmem = platform_get_drvdata(pdev); | ||
| 319 | |||
| 320 | return nvmem_unregister(nvmem); | ||
| 321 | } | ||
| 322 | |||
| 323 | static struct platform_driver bcm_otpc_driver = { | 313 | static struct platform_driver bcm_otpc_driver = { |
| 324 | .probe = bcm_otpc_probe, | 314 | .probe = bcm_otpc_probe, |
| 325 | .remove = bcm_otpc_remove, | ||
| 326 | .driver = { | 315 | .driver = { |
| 327 | .name = "brcm-otpc", | 316 | .name = "brcm-otpc", |
| 328 | .of_match_table = bcm_otpc_dt_ids, | 317 | .of_match_table = bcm_otpc_dt_ids, |
| @@ -473,9 +473,14 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config) · 472 unmodified lines | |||
| 473 | nvmem->reg_read = config->reg_read; | 473 | nvmem->reg_read = config->reg_read; |
| 474 | nvmem->reg_write = config->reg_write; | 474 | nvmem->reg_write = config->reg_write; |
| 475 | nvmem->dev.of_node = config->dev->of_node; | 475 | nvmem->dev.of_node = config->dev->of_node; |
| 476 | dev_set_name(&nvmem->dev, "%s%d", | 476 | |
| 477 | config->name ? : "nvmem", | 477 | if (config->id == -1 && config->name) { |
| 478 | config->name ? config->id : nvmem->id); | 478 | dev_set_name(&nvmem->dev, "%s", config->name); |
| 479 | } else { | ||
| 480 | dev_set_name(&nvmem->dev, "%s%d", | ||
| 481 | config->name ? : "nvmem", | ||
| 482 | config->name ? config->id : nvmem->id); | ||
| 483 | } | ||
| 479 | 484 | ||
| 480 | nvmem->read_only = device_property_present(config->dev, "read-only") | | 485 | nvmem->read_only = device_property_present(config->dev, "read-only") | |
| 481 | config->read_only; | 486 | config->read_only; |
| @@ -544,6 +549,65 @@ int nvmem_unregister(struct nvmem_device *nvmem) · 62 unmodified lines | |||
| 544 | } | 549 | } |
| 545 | EXPORT_SYMBOL_GPL(nvmem_unregister); | 550 | EXPORT_SYMBOL_GPL(nvmem_unregister); |
| 546 | 551 | ||
| 552 | static void devm_nvmem_release(struct device *dev, void *res) | ||
| 553 | { | ||
| 554 | WARN_ON(nvmem_unregister(*(struct nvmem_device **)res)); | ||
| 555 | } | ||
| 556 | |||
| 557 | /** | ||
| 558 | * devm_nvmem_register() - Register a managed nvmem device for given | ||
| 559 | * nvmem_config. | ||
| 560 | * Also creates an binary entry in /sys/bus/nvmem/devices/dev-name/nvmem | ||
| 561 | * | ||
| 562 | * @config: nvmem device configuration with which nvmem device is created. | ||
| 563 | * | ||
| 564 | * Return: Will be an ERR_PTR() on error or a valid pointer to nvmem_device | ||
| 565 | * on success. | ||
| 566 | */ | ||
| 567 | struct nvmem_device *devm_nvmem_register(struct device *dev, | ||
| 568 | const struct nvmem_config *config) | ||
| 569 | { | ||
| 570 | struct nvmem_device **ptr, *nvmem; | ||
| 571 | |||
| 572 | ptr = devres_alloc(devm_nvmem_release, sizeof(*ptr), GFP_KERNEL); | ||
| 573 | if (!ptr) | ||
| 574 | return ERR_PTR(-ENOMEM); | ||
| 575 | |||
| 576 | nvmem = nvmem_register(config); | ||
| 577 | |||
| 578 | if (!IS_ERR(nvmem)) { | ||
| 579 | *ptr = nvmem; | ||
| 580 | devres_add(dev, ptr); | ||
| 581 | } else { | ||
| 582 | devres_free(ptr); | ||
| 583 | } | ||
| 584 | |||
| 585 | return nvmem; | ||
| 586 | } | ||
| 587 | EXPORT_SYMBOL_GPL(devm_nvmem_register); | ||
| 588 | |||
| 589 | static int devm_nvmem_match(struct device *dev, void *res, void *data) | ||
| 590 | { | ||
| 591 | struct nvmem_device **r = res; | ||
| 592 | |||
| 593 | return *r == data; | ||
| 594 | } | ||
| 595 | |||
| 596 | /** | ||
| 597 | * devm_nvmem_unregister() - Unregister previously registered managed nvmem | ||
| 598 | * device. | ||
| 599 | * | ||
| 600 | * @nvmem: Pointer to previously registered nvmem device. | ||
| 601 | * | ||
| 602 | * Return: Will be an negative on error or a zero on success. | ||
| 603 | */ | ||
| 604 | int devm_nvmem_unregister(struct device *dev, struct nvmem_device *nvmem) | ||
| 605 | { | ||
| 606 | return devres_release(dev, devm_nvmem_release, devm_nvmem_match, nvmem); | ||
| 607 | } | ||
| 608 | EXPORT_SYMBOL(devm_nvmem_unregister); | ||
| 609 | |||
| 610 | |||
| 547 | static struct nvmem_device *__nvmem_device_get(struct device_node *np, | 611 | static struct nvmem_device *__nvmem_device_get(struct device_node *np, |
| 548 | struct nvmem_cell **cellp, | 612 | struct nvmem_cell **cellp, |
| 549 | const char *cell_id) | 613 | const char *cell_id) |
| @@ -125,7 +125,7 @@ static int imx_iim_probe(struct platform_device *pdev) · 124 unmodified lines | |||
| 125 | 125 | ||
| 126 | drvdata = of_id->data; | 126 | drvdata = of_id->data; |
| 127 | 127 | ||
| 128 | iim->clk = devm_clk_get(&pdev->dev, NULL); | 128 | iim->clk = devm_clk_get(dev, NULL); |
| 129 | if (IS_ERR(iim->clk)) | 129 | if (IS_ERR(iim->clk)) |
| 130 | return PTR_ERR(iim->clk); | 130 | return PTR_ERR(iim->clk); |
| 131 | 131 | ||
| @@ -138,25 +138,13 @@ static int imx_iim_probe(struct platform_device *pdev) · 6 unmodified lines | |||
| 138 | cfg.size = drvdata->nregs; | 138 | cfg.size = drvdata->nregs; |
| 139 | cfg.priv = iim; | 139 | cfg.priv = iim; |
| 140 | 140 | ||
| 141 | nvmem = nvmem_register(&cfg); | 141 | nvmem = devm_nvmem_register(dev, &cfg); |
| 142 | if (IS_ERR(nvmem)) | ||
| 143 | return PTR_ERR(nvmem); | ||
| 144 | 142 | ||
| 145 | platform_set_drvdata(pdev, nvmem); | 143 | return PTR_ERR_OR_ZERO(nvmem); |
| 146 | |||
| 147 | return 0; | ||
| 148 | } | ||
| 149 | |||
| 150 | static int imx_iim_remove(struct platform_device *pdev) | ||
| 151 | { | ||
| 152 | struct nvmem_device *nvmem = platform_get_drvdata(pdev); | ||
| 153 | |||
| 154 | return nvmem_unregister(nvmem); | ||
| 155 | } | 144 | } |
| 156 | 145 | ||
| 157 | static struct platform_driver imx_iim_driver = { | 146 | static struct platform_driver imx_iim_driver = { |
| 158 | .probe = imx_iim_probe, | 147 | .probe = imx_iim_probe, |
| 159 | .remove = imx_iim_remove, | ||
| 160 | .driver = { | 148 | .driver = { |
| 161 | .name = "imx-iim", | 149 | .name = "imx-iim", |
| 162 | .of_match_table = imx_iim_dt_ids, | 150 | .of_match_table = imx_iim_dt_ids, |
| @@ -439,7 +439,6 @@ MODULE_DEVICE_TABLE(of, imx_ocotp_dt_ids); · 438 unmodified lines | |||
| 439 | 439 | ||
| 440 | static int imx_ocotp_probe(struct platform_device *pdev) | 440 | static int imx_ocotp_probe(struct platform_device *pdev) |
| 441 | { | 441 | { |
| 442 | const struct of_device_id *of_id; | ||
| 443 | struct device *dev = &pdev->dev; | 442 | struct device *dev = &pdev->dev; |
| 444 | struct resource *res; | 443 | struct resource *res; |
| 445 | struct ocotp_priv *priv; | 444 | struct ocotp_priv *priv; |
| @@ -460,32 +459,19 @@ static int imx_ocotp_probe(struct platform_device *pdev) · 14 unmodified lines | |||
| 460 | if (IS_ERR(priv->clk)) | 459 | if (IS_ERR(priv->clk)) |
| 461 | return PTR_ERR(priv->clk); | 460 | return PTR_ERR(priv->clk); |
| 462 | 461 | ||
| 463 | of_id = of_match_device(imx_ocotp_dt_ids, dev); | ||
| 464 | priv->params = of_device_get_match_data(&pdev->dev); | 462 | priv->params = of_device_get_match_data(&pdev->dev); |
| 465 | imx_ocotp_nvmem_config.size = 4 * priv->params->nregs; | 463 | imx_ocotp_nvmem_config.size = 4 * priv->params->nregs; |
| 466 | imx_ocotp_nvmem_config.dev = dev; | 464 | imx_ocotp_nvmem_config.dev = dev; |
| 467 | imx_ocotp_nvmem_config.priv = priv; | 465 | imx_ocotp_nvmem_config.priv = priv; |
| 468 | priv->config = &imx_ocotp_nvmem_config; | 466 | priv->config = &imx_ocotp_nvmem_config; |
| 469 | nvmem = nvmem_register(&imx_ocotp_nvmem_config); | 467 | nvmem = devm_nvmem_register(dev, &imx_ocotp_nvmem_config); |
| 470 | 468 | ||
| 471 | if (IS_ERR(nvmem)) | ||
| 472 | return PTR_ERR(nvmem); | ||
| 473 | 469 | ||
| 474 | platform_set_drvdata(pdev, nvmem); | 470 | return PTR_ERR_OR_ZERO(nvmem); |
| 475 | |||
| 476 | return 0; | ||
| 477 | } | ||
| 478 | |||
| 479 | static int imx_ocotp_remove(struct platform_device *pdev) | ||
| 480 | { | ||
| 481 | struct nvmem_device *nvmem = platform_get_drvdata(pdev); | ||
| 482 | |||
| 483 | return nvmem_unregister(nvmem); | ||
| 484 | } | 471 | } |
| 485 | 472 | ||
| 486 | static struct platform_driver imx_ocotp_driver = { | 473 | static struct platform_driver imx_ocotp_driver = { |
| 487 | .probe = imx_ocotp_probe, | 474 | .probe = imx_ocotp_probe, |
| 488 | .remove = imx_ocotp_remove, | ||
| 489 | .driver = { | 475 | .driver = { |
| 490 | .name = "imx_ocotp", | 476 | .name = "imx_ocotp", |
| 491 | .of_match_table = imx_ocotp_dt_ids, | 477 | .of_match_table = imx_ocotp_dt_ids, |
| @@ -86,20 +86,9 @@ static int lpc18xx_otp_probe(struct platform_device *pdev) · 85 unmodified lines | |||
| 86 | lpc18xx_otp_nvmem_config.dev = &pdev->dev; | 86 | lpc18xx_otp_nvmem_config.dev = &pdev->dev; |
| 87 | lpc18xx_otp_nvmem_config.priv = otp; | 87 | lpc18xx_otp_nvmem_config.priv = otp; |
| 88 | 88 | ||
| 89 | nvmem = nvmem_register(&lpc18xx_otp_nvmem_config); | 89 | nvmem = devm_nvmem_register(&pdev->dev, &lpc18xx_otp_nvmem_config); |
| 90 | if (IS_ERR(nvmem)) | ||
| 91 | return PTR_ERR(nvmem); | ||
| 92 | 90 | ||
| 93 | platform_set_drvdata(pdev, nvmem); | 91 | return PTR_ERR_OR_ZERO(nvmem); |
| 94 | |||
| 95 | return 0; | ||
| 96 | } | ||
| 97 | |||
| 98 | static int lpc18xx_otp_remove(struct platform_device *pdev) | ||
| 99 | { | ||
| 100 | struct nvmem_device *nvmem = platform_get_drvdata(pdev); | ||
| 101 | |||
| 102 | return nvmem_unregister(nvmem); | ||
| 103 | } | 92 | } |
| 104 | 93 | ||
| 105 | static const struct of_device_id lpc18xx_otp_dt_ids[] = { | 94 | static const struct of_device_id lpc18xx_otp_dt_ids[] = { |
| @@ -110,7 +99,6 @@ MODULE_DEVICE_TABLE(of, lpc18xx_otp_dt_ids); · 4 unmodified lines | |||
| 110 | 99 | ||
| 111 | static struct platform_driver lpc18xx_otp_driver = { | 100 | static struct platform_driver lpc18xx_otp_driver = { |
| 112 | .probe = lpc18xx_otp_probe, | 101 | .probe = lpc18xx_otp_probe, |
| 113 | .remove = lpc18xx_otp_remove, | ||
| 114 | .driver = { | 102 | .driver = { |
| 115 | .name = "lpc18xx_otp", | 103 | .name = "lpc18xx_otp", |
| 116 | .of_match_table = lpc18xx_otp_dt_ids, | 104 | .of_match_table = lpc18xx_otp_dt_ids, |
| @@ -60,25 +60,13 @@ static int meson_efuse_probe(struct platform_device *pdev) · 59 unmodified lines | |||
| 60 | econfig.reg_read = meson_efuse_read; | 60 | econfig.reg_read = meson_efuse_read; |
| 61 | econfig.size = size; | 61 | econfig.size = size; |
| 62 | 62 | ||
| 63 | nvmem = nvmem_register(&econfig); | 63 | nvmem = devm_nvmem_register(&pdev->dev, &econfig); |
| 64 | if (IS_ERR(nvmem)) | ||
| 65 | return PTR_ERR(nvmem); | ||
| 66 | 64 | ||
| 67 | platform_set_drvdata(pdev, nvmem); | 65 | return PTR_ERR_OR_ZERO(nvmem); |
| 68 | |||
| 69 | return 0; | ||
| 70 | } | ||
| 71 | |||
| 72 | static int meson_efuse_remove(struct platform_device *pdev) | ||
| 73 | { | ||
| 74 | struct nvmem_device *nvmem = platform_get_drvdata(pdev); | ||
| 75 | |||
| 76 | return nvmem_unregister(nvmem); | ||
| 77 | } | 66 | } |
| 78 | 67 | ||
| 79 | static struct platform_driver meson_efuse_driver = { | 68 | static struct platform_driver meson_efuse_driver = { |
| 80 | .probe = meson_efuse_probe, | 69 | .probe = meson_efuse_probe, |
| 81 | .remove = meson_efuse_remove, | ||
| 82 | .driver = { | 70 | .driver = { |
| 83 | .name = "meson-efuse", | 71 | .name = "meson-efuse", |
| 84 | .of_match_table = meson_efuse_match, | 72 | .of_match_table = meson_efuse_match, |
| @@ -233,25 +233,13 @@ static int meson_mx_efuse_probe(struct platform_device *pdev) · 232 unmodified lines | |||
| 233 | return PTR_ERR(efuse->core_clk); | 233 | return PTR_ERR(efuse->core_clk); |
| 234 | } | 234 | } |
| 235 | 235 | ||
| 236 | efuse->nvmem = nvmem_register(&efuse->config); | 236 | efuse->nvmem = devm_nvmem_register(&pdev->dev, &efuse->config); |
| 237 | if (IS_ERR(efuse->nvmem)) | ||
| 238 | return PTR_ERR(efuse->nvmem); | ||
| 239 | 237 | ||
| 240 | platform_set_drvdata(pdev, efuse); | 238 | return PTR_ERR_OR_ZERO(efuse->nvmem); |
| 241 | |||
| 242 | return 0; | ||
| 243 | } | ||
| 244 | |||
| 245 | static int meson_mx_efuse_remove(struct platform_device *pdev) | ||
| 246 | { | ||
| 247 | struct meson_mx_efuse *efuse = platform_get_drvdata(pdev); | ||
| 248 | |||
| 249 | return nvmem_unregister(efuse->nvmem); | ||
| 250 | } | 239 | } |
| 251 | 240 | ||
| 252 | static struct platform_driver meson_mx_efuse_driver = { | 241 | static struct platform_driver meson_mx_efuse_driver = { |
| 253 | .probe = meson_mx_efuse_probe, | 242 | .probe = meson_mx_efuse_probe, |
| 254 | .remove = meson_mx_efuse_remove, | ||
| 255 | .driver = { | 243 | .driver = { |
| 256 | .name = "meson-mx-efuse", | 244 | .name = "meson-mx-efuse", |
| 257 | .of_match_table = meson_mx_efuse_match, | 245 | .of_match_table = meson_mx_efuse_match, |
| @@ -72,20 +72,9 @@ static int mtk_efuse_probe(struct platform_device *pdev) · 71 unmodified lines | |||
| 72 | econfig.size = resource_size(res); | 72 | econfig.size = resource_size(res); |
| 73 | econfig.priv = priv; | 73 | econfig.priv = priv; |
| 74 | econfig.dev = dev; | 74 | econfig.dev = dev; |
| 75 | nvmem = nvmem_register(&econfig); | 75 | nvmem = devm_nvmem_register(dev, &econfig); |
| 76 | if (IS_ERR(nvmem)) | ||
| 77 | return PTR_ERR(nvmem); | ||
| 78 | 76 | ||
| 79 | platform_set_drvdata(pdev, nvmem); | 77 | return PTR_ERR_OR_ZERO(nvmem); |
| 80 | |||
| 81 | return 0; | ||
| 82 | } | ||
| 83 | |||
| 84 | static int mtk_efuse_remove(struct platform_device *pdev) | ||
| 85 | { | ||
| 86 | struct nvmem_device *nvmem = platform_get_drvdata(pdev); | ||
| 87 | |||
| 88 | return nvmem_unregister(nvmem); | ||
| 89 | } | 78 | } |
| 90 | 79 | ||
| 91 | static const struct of_device_id mtk_efuse_of_match[] = { | 80 | static const struct of_device_id mtk_efuse_of_match[] = { |
| @@ -97,7 +86,6 @@ MODULE_DEVICE_TABLE(of, mtk_efuse_of_match); · 5 unmodified lines | |||
| 97 | 86 | ||
| 98 | static struct platform_driver mtk_efuse_driver = { | 87 | static struct platform_driver mtk_efuse_driver = { |
| 99 | .probe = mtk_efuse_probe, | 88 | .probe = mtk_efuse_probe, |
| 100 | .remove = mtk_efuse_remove, | ||
| 101 | .driver = { | 89 | .driver = { |
| 102 | .name = "mediatek,efuse", | 90 | .name = "mediatek,efuse", |
| 103 | .of_match_table = mtk_efuse_of_match, | 91 | .of_match_table = mtk_efuse_of_match, |
| @@ -47,13 +47,6 @@ static int qfprom_reg_write(void *context, · 46 unmodified lines | |||
| 47 | return 0; | 47 | return 0; |
| 48 | } | 48 | } |
| 49 | 49 | ||
| 50 | static int qfprom_remove(struct platform_device *pdev) | ||
| 51 | { | ||
| 52 | struct nvmem_device *nvmem = platform_get_drvdata(pdev); | ||
| 53 | |||
| 54 | return nvmem_unregister(nvmem); | ||
| 55 | } | ||
| 56 | |||
| 57 | static struct nvmem_config econfig = { | 50 | static struct nvmem_config econfig = { |
| 58 | .name = "qfprom", | 51 | .name = "qfprom", |
| 59 | .stride = 1, | 52 | .stride = 1, |
| @@ -82,13 +75,9 @@ static int qfprom_probe(struct platform_device *pdev) · 22 unmodified lines | |||
| 82 | econfig.dev = dev; | 75 | econfig.dev = dev; |
| 83 | econfig.priv = priv; | 76 | econfig.priv = priv; |
| 84 | 77 | ||
| 85 | nvmem = nvmem_register(&econfig); | 78 | nvmem = devm_nvmem_register(dev, &econfig); |
| 86 | if (IS_ERR(nvmem)) | ||
| 87 | return PTR_ERR(nvmem); | ||
| 88 | |||
| 89 | platform_set_drvdata(pdev, nvmem); | ||
| 90 | 79 | ||
| 91 | return 0; | 80 | return PTR_ERR_OR_ZERO(nvmem); |
| 92 | } | 81 | } |
| 93 | 82 | ||
| 94 | static const struct of_device_id qfprom_of_match[] = { | 83 | static const struct of_device_id qfprom_of_match[] = { |
| @@ -99,7 +88,6 @@ MODULE_DEVICE_TABLE(of, qfprom_of_match); · 4 unmodified lines | |||
| 99 | 88 | ||
| 100 | static struct platform_driver qfprom_driver = { | 89 | static struct platform_driver qfprom_driver = { |
| 101 | .probe = qfprom_probe, | 90 | .probe = qfprom_probe, |
| 102 | .remove = qfprom_remove, | ||
| 103 | .driver = { | 91 | .driver = { |
| 104 | .name = "qcom,qfprom", | 92 | .name = "qcom,qfprom", |
| 105 | .of_match_table = qfprom_of_match, | 93 | .of_match_table = qfprom_of_match, |
| @@ -259,55 +259,43 @@ static int rockchip_efuse_probe(struct platform_device *pdev) · 258 unmodified lines | |||
| 259 | struct resource *res; | 259 | struct resource *res; |
| 260 | struct nvmem_device *nvmem; | 260 | struct nvmem_device *nvmem; |
| 261 | struct rockchip_efuse_chip *efuse; | 261 | struct rockchip_efuse_chip *efuse; |
| 262 | const struct of_device_id *match; | 262 | const void *data; |
| 263 | struct device *dev = &pdev->dev; | 263 | struct device *dev = &pdev->dev; |
| 264 | 264 | ||
| 265 | match = of_match_device(dev->driver->of_match_table, dev); | 265 | data = of_device_get_match_data(dev); |
| 266 | if (!match || !match->data) { | 266 | if (!data) { |
| 267 | dev_err(dev, "failed to get match data\n"); | 267 | dev_err(dev, "failed to get match data\n"); |
| 268 | return -EINVAL; | 268 | return -EINVAL; |
| 269 | } | 269 | } |
| 270 | 270 | ||
| 271 | efuse = devm_kzalloc(&pdev->dev, sizeof(struct rockchip_efuse_chip), | 271 | efuse = devm_kzalloc(dev, sizeof(struct rockchip_efuse_chip), |
| 272 | GFP_KERNEL); | 272 | GFP_KERNEL); |
| 273 | if (!efuse) | 273 | if (!efuse) |
| 274 | return -ENOMEM; | 274 | return -ENOMEM; |
| 275 | 275 | ||
| 276 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 276 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
| 277 | efuse->base = devm_ioremap_resource(&pdev->dev, res); | 277 | efuse->base = devm_ioremap_resource(dev, res); |
| 278 | if (IS_ERR(efuse->base)) | 278 | if (IS_ERR(efuse->base)) |
| 279 | return PTR_ERR(efuse->base); | 279 | return PTR_ERR(efuse->base); |
| 280 | 280 | ||
| 281 | efuse->clk = devm_clk_get(&pdev->dev, "pclk_efuse"); | 281 | efuse->clk = devm_clk_get(dev, "pclk_efuse"); |
| 282 | if (IS_ERR(efuse->clk)) | 282 | if (IS_ERR(efuse->clk)) |
| 283 | return PTR_ERR(efuse->clk); | 283 | return PTR_ERR(efuse->clk); |
| 284 | 284 | ||
| 285 | efuse->dev = &pdev->dev; | 285 | efuse->dev = dev; |
| 286 | if (of_property_read_u32(dev->of_node, "rockchip,efuse-size", | 286 | if (of_property_read_u32(dev->of_node, "rockchip,efuse-size", |
| 287 | &econfig.size)) | 287 | &econfig.size)) |
| 288 | econfig.size = resource_size(res); | 288 | econfig.size = resource_size(res); |
| 289 | econfig.reg_read = match->data; | 289 | econfig.reg_read = data; |
| 290 | econfig.priv = efuse; | 290 | econfig.priv = efuse; |
| 291 | econfig.dev = efuse->dev; | 291 | econfig.dev = efuse->dev; |
| 292 | nvmem = nvmem_register(&econfig); | 292 | nvmem = devm_nvmem_register(dev, &econfig); |
| 293 | if (IS_ERR(nvmem)) | ||
| 294 | return PTR_ERR(nvmem); | ||
| 295 | 293 | ||
| 296 | platform_set_drvdata(pdev, nvmem); | 294 | return PTR_ERR_OR_ZERO(nvmem); |
| 297 | |||
| 298 | return 0; | ||
| 299 | } | ||
| 300 | |||
| 301 | static int rockchip_efuse_remove(struct platform_device *pdev) | ||
| 302 | { | ||
| 303 | struct nvmem_device *nvmem = platform_get_drvdata(pdev); | ||
| 304 | |||
| 305 | return nvmem_unregister(nvmem); | ||
| 306 | } | 295 | } |
| 307 | 296 | ||
| 308 | static struct platform_driver rockchip_efuse_driver = { | 297 | static struct platform_driver rockchip_efuse_driver = { |
| 309 | .probe = rockchip_efuse_probe, | 298 | .probe = rockchip_efuse_probe, |
| 310 | .remove = rockchip_efuse_remove, | ||
| 311 | .driver = { | 299 | .driver = { |
| 312 | .name = "rockchip-efuse", | 300 | .name = "rockchip-efuse", |
| 313 | .of_match_table = rockchip_efuse_match, | 301 | .of_match_table = rockchip_efuse_match, |
| @@ -14,15 +14,21 @@ · 13 unmodified lines | |||
| 14 | #include <linux/regmap.h> | 14 | #include <linux/regmap.h> |
| 15 | 15 | ||
| 16 | #define IMX6Q_SNVS_HPLR 0x00 | 16 | #define IMX6Q_SNVS_HPLR 0x00 |
| 17 | #define IMX6Q_GPR_SL BIT(5) | ||
| 18 | #define IMX6Q_SNVS_LPLR 0x34 | 17 | #define IMX6Q_SNVS_LPLR 0x34 |
| 19 | #define IMX6Q_GPR_HL BIT(5) | ||
| 20 | #define IMX6Q_SNVS_LPGPR 0x68 | 18 | #define IMX6Q_SNVS_LPGPR 0x68 |
| 21 | 19 | ||
| 20 | #define IMX7D_SNVS_HPLR 0x00 | ||
| 21 | #define IMX7D_SNVS_LPLR 0x34 | ||
| 22 | #define IMX7D_SNVS_LPGPR 0x90 | ||
| 23 | |||
| 24 | #define IMX_GPR_SL BIT(5) | ||
| 25 | #define IMX_GPR_HL BIT(5) | ||
| 26 | |||
| 22 | struct snvs_lpgpr_cfg { | 27 | struct snvs_lpgpr_cfg { |
| 23 | int offset; | 28 | int offset; |
| 24 | int offset_hplr; | 29 | int offset_hplr; |
| 25 | int offset_lplr; | 30 | int offset_lplr; |
| 31 | int size; | ||
| 26 | }; | 32 | }; |
| 27 | 33 | ||
| 28 | struct snvs_lpgpr_priv { | 34 | struct snvs_lpgpr_priv { |
| @@ -36,6 +42,14 @@ static const struct snvs_lpgpr_cfg snvs_lpgpr_cfg_imx6q = { · 7 unmodified lines | |||
| 36 | .offset = IMX6Q_SNVS_LPGPR, | 42 | .offset = IMX6Q_SNVS_LPGPR, |
| 37 | .offset_hplr = IMX6Q_SNVS_HPLR, | 43 | .offset_hplr = IMX6Q_SNVS_HPLR, |
| 38 | .offset_lplr = IMX6Q_SNVS_LPLR, | 44 | .offset_lplr = IMX6Q_SNVS_LPLR, |
| 45 | .size = 4, | ||
| 46 | }; | ||
| 47 | |||
| 48 | static const struct snvs_lpgpr_cfg snvs_lpgpr_cfg_imx7d = { | ||
| 49 | .offset = IMX7D_SNVS_LPGPR, | ||
| 50 | .offset_hplr = IMX7D_SNVS_HPLR, | ||
| 51 | .offset_lplr = IMX7D_SNVS_LPLR, | ||
| 52 | .size = 16, | ||
| 39 | }; | 53 | }; |
| 40 | 54 | ||
| 41 | static int snvs_lpgpr_write(void *context, unsigned int offset, void *val, | 55 | static int snvs_lpgpr_write(void *context, unsigned int offset, void *val, |
| @@ -50,14 +64,14 @@ static int snvs_lpgpr_write(void *context, unsigned int offset, void *val, · 8 unmodified lines | |||
| 50 | if (ret < 0) | 64 | if (ret < 0) |
| 51 | return ret; | 65 | return ret; |
| 52 | 66 | ||
| 53 | if (lock_reg & IMX6Q_GPR_SL) | 67 | if (lock_reg & IMX_GPR_SL) |
| 54 | return -EPERM; | 68 | return -EPERM; |
| 55 | 69 | ||
| 56 | ret = regmap_read(priv->regmap, dcfg->offset_lplr, &lock_reg); | 70 | ret = regmap_read(priv->regmap, dcfg->offset_lplr, &lock_reg); |
| 57 | if (ret < 0) | 71 | if (ret < 0) |
| 58 | return ret; | 72 | return ret; |
| 59 | 73 | ||
| 60 | if (lock_reg & IMX6Q_GPR_HL) | 74 | if (lock_reg & IMX_GPR_HL) |
| 61 | return -EPERM; | 75 | return -EPERM; |
| 62 | 76 | ||
| 63 | return regmap_bulk_write(priv->regmap, dcfg->offset + offset, val, | 77 | return regmap_bulk_write(priv->regmap, dcfg->offset + offset, val, |
| @@ -110,40 +124,29 @@ static int snvs_lpgpr_probe(struct platform_device *pdev) · 46 unmodified lines | |||
| 110 | cfg->priv = priv; | 124 | cfg->priv = priv; |
| 111 | cfg->name = dev_name(dev); | 125 | cfg->name = dev_name(dev); |
| 112 | cfg->dev = dev; | 126 | cfg->dev = dev; |
| 113 | cfg->stride = 4, | 127 | cfg->stride = 4; |
| 114 | cfg->word_size = 4, | 128 | cfg->word_size = 4; |
| 115 | cfg->size = 4, | 129 | cfg->size = dcfg->size, |
| 116 | cfg->owner = THIS_MODULE, | 130 | cfg->owner = THIS_MODULE; |
| 117 | cfg->reg_read = snvs_lpgpr_read, | 131 | cfg->reg_read = snvs_lpgpr_read; |
| 118 | cfg->reg_write = snvs_lpgpr_write, | 132 | cfg->reg_write = snvs_lpgpr_write; |
| 119 | |||
| 120 | nvmem = nvmem_register(cfg); | ||
| 121 | if (IS_ERR(nvmem)) | ||
| 122 | return PTR_ERR(nvmem); | ||
| 123 | 133 | ||
| 124 | platform_set_drvdata(pdev, nvmem); | 134 | nvmem = devm_nvmem_register(dev, cfg); |
| 125 | |||
| 126 | return 0; | ||
| 127 | } | ||
| 128 | |||
| 129 | static int snvs_lpgpr_remove(struct platform_device *pdev) | ||
| 130 | { | ||
| 131 | struct nvmem_device *nvmem = platform_get_drvdata(pdev); | ||
| 132 | 135 | ||
| 133 | return nvmem_unregister(nvmem); | 136 | return PTR_ERR_OR_ZERO(nvmem); |
| 134 | } | 137 | } |
| 135 | 138 | ||
| 136 | static const struct of_device_id snvs_lpgpr_dt_ids[] = { | 139 | static const struct of_device_id snvs_lpgpr_dt_ids[] = { |
| 137 | { .compatible = "fsl,imx6q-snvs-lpgpr", .data = &snvs_lpgpr_cfg_imx6q }, | 140 | { .compatible = "fsl,imx6q-snvs-lpgpr", .data = &snvs_lpgpr_cfg_imx6q }, |
| 138 | { .compatible = "fsl,imx6ul-snvs-lpgpr", | 141 | { .compatible = "fsl,imx6ul-snvs-lpgpr", |
| 139 | .data = &snvs_lpgpr_cfg_imx6q }, | 142 | .data = &snvs_lpgpr_cfg_imx6q }, |
| 143 | { .compatible = "fsl,imx7d-snvs-lpgpr", .data = &snvs_lpgpr_cfg_imx7d }, | ||
| 140 | { }, | 144 | { }, |
| 141 | }; | 145 | }; |
| 142 | MODULE_DEVICE_TABLE(of, snvs_lpgpr_dt_ids); | 146 | MODULE_DEVICE_TABLE(of, snvs_lpgpr_dt_ids); |
| 143 | 147 | ||
| 144 | static struct platform_driver snvs_lpgpr_driver = { | 148 | static struct platform_driver snvs_lpgpr_driver = { |
| 145 | .probe = snvs_lpgpr_probe, | 149 | .probe = snvs_lpgpr_probe, |
| 146 | .remove = snvs_lpgpr_remove, | ||
| 147 | .driver = { | 150 | .driver = { |
| 148 | .name = "snvs_lpgpr", | 151 | .name = "snvs_lpgpr", |
| 149 | .of_match_table = snvs_lpgpr_dt_ids, | 152 | .of_match_table = snvs_lpgpr_dt_ids, |
| @@ -152,5 +155,5 @@ static struct platform_driver snvs_lpgpr_driver = { · 2 unmodified lines | |||
| 152 | module_platform_driver(snvs_lpgpr_driver); | 155 | module_platform_driver(snvs_lpgpr_driver); |
| 153 | 156 | ||
| 154 | MODULE_AUTHOR("Oleksij Rempel <o.rempel@pengutronix.de>"); | 157 | MODULE_AUTHOR("Oleksij Rempel <o.rempel@pengutronix.de>"); |
| 155 | MODULE_DESCRIPTION("Low Power General Purpose Register in i.MX6 Secure Non-Volatile Storage"); | 158 | MODULE_DESCRIPTION("Low Power General Purpose Register in i.MX6 and i.MX7 Secure Non-Volatile Storage"); |
| 156 | MODULE_LICENSE("GPL v2"); | 159 | MODULE_LICENSE("GPL v2"); |
| @@ -85,13 +85,14 @@ static int sunxi_sid_read(void *context, unsigned int offset, · 84 unmodified lines | |||
| 85 | } | 85 | } |
| 86 | 86 | ||
| 87 | static int sun8i_sid_register_readout(const struct sunxi_sid *sid, | 87 | static int sun8i_sid_register_readout(const struct sunxi_sid *sid, |
| 88 | const unsigned int word) | 88 | const unsigned int offset, |
| 89 | u32 *out) | ||
| 89 | { | 90 | { |
| 90 | u32 reg_val; | 91 | u32 reg_val; |
| 91 | int ret; | 92 | int ret; |
| 92 | 93 | ||
| 93 | /* Set word, lock access, and set read command */ | 94 | /* Set word, lock access, and set read command */ |
| 94 | reg_val = (word & SUN8I_SID_OFFSET_MASK) | 95 | reg_val = (offset & SUN8I_SID_OFFSET_MASK) |
| 95 | << SUN8I_SID_OFFSET_SHIFT; | 96 | << SUN8I_SID_OFFSET_SHIFT; |
| 96 | reg_val |= SUN8I_SID_OP_LOCK | SUN8I_SID_READ; | 97 | reg_val |= SUN8I_SID_OP_LOCK | SUN8I_SID_READ; |
| 97 | writel(reg_val, sid->base + SUN8I_SID_PRCTL); | 98 | writel(reg_val, sid->base + SUN8I_SID_PRCTL); |
| @@ -101,7 +102,49 @@ static int sun8i_sid_register_readout(const struct sunxi_sid *sid, · 3 unmodified lines | |||
| 101 | if (ret) | 102 | if (ret) |
| 102 | return ret; | 103 | return ret; |
| 103 | 104 | ||
| 105 | if (out) | ||
| 106 | *out = readl(sid->base + SUN8I_SID_RDKEY); | ||
| 107 | |||
| 104 | writel(0, sid->base + SUN8I_SID_PRCTL); | 108 | writel(0, sid->base + SUN8I_SID_PRCTL); |
| 109 | |||
| 110 | return 0; | ||
| 111 | } | ||
| 112 | |||
| 113 | /* | ||
| 114 | * On Allwinner H3, the value on the 0x200 offset of the SID controller seems | ||
| 115 | * to be not reliable at all. | ||
| 116 | * Read by the registers instead. | ||
| 117 | */ | ||
| 118 | static int sun8i_sid_read_byte_by_reg(const struct sunxi_sid *sid, | ||
| 119 | const unsigned int offset, | ||
| 120 | u8 *out) | ||
| 121 | { | ||
| 122 | u32 word; | ||
| 123 | int ret; | ||
| 124 | |||
| 125 | ret = sun8i_sid_register_readout(sid, offset & ~0x03, &word); | ||
| 126 | |||
| 127 | if (ret) | ||
| 128 | return ret; | ||
| 129 | |||
| 130 | *out = (word >> ((offset & 0x3) * 8)) & 0xff; | ||
| 131 | |||
| 132 | return 0; | ||
| 133 | } | ||
| 134 | |||
| 135 | static int sun8i_sid_read_by_reg(void *context, unsigned int offset, | ||
| 136 | void *val, size_t bytes) | ||
| 137 | { | ||
| 138 | struct sunxi_sid *sid = context; | ||
| 139 | u8 *buf = val; | ||
| 140 | int ret; | ||
| 141 | |||
| 142 | while (bytes--) { | ||
| 143 | ret = sun8i_sid_read_byte_by_reg(sid, offset++, buf++); | ||
| 144 | if (ret) | ||
| 145 | return ret; | ||
| 146 | } | ||
| 147 | |||
| 105 | return 0; | 148 | return 0; |
| 106 | } | 149 | } |
| 107 | 150 | ||
| @@ -131,26 +174,12 @@ static int sunxi_sid_probe(struct platform_device *pdev) · 23 unmodified lines | |||
| 131 | 174 | ||
| 132 | size = cfg->size; | 175 | size = cfg->size; |
| 133 | 176 | ||
| 134 | if (cfg->need_register_readout) { | ||
| 135 | /* | ||
| 136 | * H3's SID controller have a bug that the value at 0x200 | ||
| 137 | * offset is not the correct value when the hardware is reseted. | ||
| 138 | * However, after doing a register-based read operation, the | ||
| 139 | * value become right. | ||
| 140 | * Do a full read operation here, but ignore its value | ||
| 141 | * (as it's more fast to read by direct MMIO value than | ||
| 142 | * with registers) | ||
| 143 | */ | ||
| 144 | for (i = 0; i < (size >> 2); i++) { | ||
| 145 | ret = sun8i_sid_register_readout(sid, i); | ||
| 146 | if (ret) | ||
| 147 | return ret; | ||
| 148 | } | ||
| 149 | } | ||
| 150 | |||
| 151 | econfig.size = size; | 177 | econfig.size = size; |
| 152 | econfig.dev = dev; | 178 | econfig.dev = dev; |
| 153 | econfig.reg_read = sunxi_sid_read; | 179 | if (cfg->need_register_readout) |
| 180 | econfig.reg_read = sun8i_sid_read_by_reg; | ||
| 181 | else | ||
| 182 | econfig.reg_read = sunxi_sid_read; | ||
| 154 | econfig.priv = sid; | 183 | econfig.priv = sid; |
| 155 | nvmem = nvmem_register(&econfig); | 184 | nvmem = nvmem_register(&econfig); |
| 156 | if (IS_ERR(nvmem)) | 185 | if (IS_ERR(nvmem)) |
| @@ -163,7 +192,7 @@ static int sunxi_sid_probe(struct platform_device *pdev) · 6 unmodified lines | |||
| 163 | } | 192 | } |
| 164 | 193 | ||
| 165 | for (i = 0; i < size; i++) | 194 | for (i = 0; i < size; i++) |
| 166 | randomness[i] = sunxi_sid_read_byte(sid, i); | 195 | econfig.reg_read(sid, i, &randomness[i], 1); |
| 167 | 196 | ||
| 168 | add_device_randomness(randomness, size); | 197 | add_device_randomness(randomness, size); |
| 169 | kfree(randomness); | 198 | kfree(randomness); |
| @@ -60,20 +60,9 @@ static int uniphier_efuse_probe(struct platform_device *pdev) · 59 unmodified lines | |||
| 60 | econfig.size = resource_size(res); | 60 | econfig.size = resource_size(res); |
| 61 | econfig.priv = priv; | 61 | econfig.priv = priv; |
| 62 | econfig.dev = dev; | 62 | econfig.dev = dev; |
| 63 | nvmem = nvmem_register(&econfig); | 63 | nvmem = devm_nvmem_register(dev, &econfig); |
| 64 | if (IS_ERR(nvmem)) | ||
| 65 | return PTR_ERR(nvmem); | ||
| 66 | 64 | ||
| 67 | platform_set_drvdata(pdev, nvmem); | 65 | return PTR_ERR_OR_ZERO(nvmem); |
| 68 | |||
| 69 | return 0; | ||
| 70 | } | ||
| 71 | |||
| 72 | static int uniphier_efuse_remove(struct platform_device *pdev) | ||
| 73 | { | ||
| 74 | struct nvmem_device *nvmem = platform_get_drvdata(pdev); | ||
| 75 | |||
| 76 | return nvmem_unregister(nvmem); | ||
| 77 | } | 66 | } |
| 78 | 67 | ||
| 79 | static const struct of_device_id uniphier_efuse_of_match[] = { | 68 | static const struct of_device_id uniphier_efuse_of_match[] = { |
| @@ -84,7 +73,6 @@ MODULE_DEVICE_TABLE(of, uniphier_efuse_of_match); · 4 unmodified lines | |||
| 84 | 73 | ||
| 85 | static struct platform_driver uniphier_efuse_driver = { | 74 | static struct platform_driver uniphier_efuse_driver = { |
| 86 | .probe = uniphier_efuse_probe, | 75 | .probe = uniphier_efuse_probe, |
| 87 | .remove = uniphier_efuse_remove, | ||
| 88 | .driver = { | 76 | .driver = { |
| 89 | .name = "uniphier-efuse", | 77 | .name = "uniphier-efuse", |
| 90 | .of_match_table = uniphier_efuse_of_match, | 78 | .of_match_table = uniphier_efuse_of_match, |
| @@ -217,21 +217,13 @@ static const struct of_device_id ocotp_of_match[] = { · 216 unmodified lines | |||
| 217 | }; | 217 | }; |
| 218 | MODULE_DEVICE_TABLE(of, ocotp_of_match); | 218 | MODULE_DEVICE_TABLE(of, ocotp_of_match); |
| 219 | 219 | ||
| 220 | static int vf610_ocotp_remove(struct platform_device *pdev) | ||
| 221 | { | ||
| 222 | struct vf610_ocotp *ocotp_dev = platform_get_drvdata(pdev); | ||
| 223 | |||
| 224 | return nvmem_unregister(ocotp_dev->nvmem); | ||
| 225 | } | ||
| 226 | |||
| 227 | static int vf610_ocotp_probe(struct platform_device *pdev) | 220 | static int vf610_ocotp_probe(struct platform_device *pdev) |
| 228 | { | 221 | { |
| 229 | struct device *dev = &pdev->dev; | 222 | struct device *dev = &pdev->dev; |
| 230 | struct resource *res; | 223 | struct resource *res; |
| 231 | struct vf610_ocotp *ocotp_dev; | 224 | struct vf610_ocotp *ocotp_dev; |
| 232 | 225 | ||
| 233 | ocotp_dev = devm_kzalloc(&pdev->dev, | 226 | ocotp_dev = devm_kzalloc(dev, sizeof(struct vf610_ocotp), GFP_KERNEL); |
| 234 | sizeof(struct vf610_ocotp), GFP_KERNEL); | ||
| 235 | if (!ocotp_dev) | 227 | if (!ocotp_dev) |
| 236 | return -ENOMEM; | 228 | return -ENOMEM; |
| 237 | 229 | ||
| @@ -246,26 +238,20 @@ static int vf610_ocotp_probe(struct platform_device *pdev) · 8 unmodified lines | |||
| 246 | PTR_ERR(ocotp_dev->clk)); | 238 | PTR_ERR(ocotp_dev->clk)); |
| 247 | return PTR_ERR(ocotp_dev->clk); | 239 | return PTR_ERR(ocotp_dev->clk); |
| 248 | } | 240 | } |
| 241 | ocotp_dev->dev = dev; | ||
| 242 | ocotp_dev->timing = vf610_ocotp_calculate_timing(ocotp_dev); | ||
| 249 | 243 | ||
| 250 | ocotp_config.size = resource_size(res); | 244 | ocotp_config.size = resource_size(res); |
| 251 | ocotp_config.priv = ocotp_dev; | 245 | ocotp_config.priv = ocotp_dev; |
| 252 | ocotp_config.dev = dev; | 246 | ocotp_config.dev = dev; |
| 253 | 247 | ||
| 254 | ocotp_dev->nvmem = nvmem_register(&ocotp_config); | 248 | ocotp_dev->nvmem = devm_nvmem_register(dev, &ocotp_config); |
| 255 | if (IS_ERR(ocotp_dev->nvmem)) | ||
| 256 | return PTR_ERR(ocotp_dev->nvmem); | ||
| 257 | 249 | ||
| 258 | ocotp_dev->dev = dev; | 250 | return PTR_ERR_OR_ZERO(ocotp_dev->nvmem); |
| 259 | platform_set_drvdata(pdev, ocotp_dev); | ||
| 260 | |||
| 261 | ocotp_dev->timing = vf610_ocotp_calculate_timing(ocotp_dev); | ||
| 262 | |||
| 263 | return 0; | ||
| 264 | } | 251 | } |
| 265 | 252 | ||
| 266 | static struct platform_driver vf610_ocotp_driver = { | 253 | static struct platform_driver vf610_ocotp_driver = { |
| 267 | .probe = vf610_ocotp_probe, | 254 | .probe = vf610_ocotp_probe, |
| 268 | .remove = vf610_ocotp_remove, | ||
| 269 | .driver = { | 255 | .driver = { |
| 270 | .name = "vf610-ocotp", | 256 | .name = "vf610-ocotp", |
| 271 | .of_match_table = ocotp_of_match, | 257 | .of_match_table = ocotp_of_match, |
| @@ -273,18 +273,16 @@ static int parport_ax88796_probe(struct platform_device *pdev) · 272 unmodified lines | |||
| 273 | { | 273 | { |
| 274 | struct device *_dev = &pdev->dev; | 274 | struct device *_dev = &pdev->dev; |
| 275 | struct ax_drvdata *dd; | 275 | struct ax_drvdata *dd; |
| 276 | struct parport *pp = NULL; | 276 | struct parport *pp; |
| 277 | struct resource *res; | 277 | struct resource *res; |
| 278 | unsigned long size; | 278 | unsigned long size; |
| 279 | int spacing; | 279 | int spacing; |
| 280 | int irq; | 280 | int irq; |
| 281 | int ret; | 281 | int ret; |
| 282 | 282 | ||
| 283 | dd = kzalloc(sizeof(struct ax_drvdata), GFP_KERNEL); | 283 | dd = kzalloc(sizeof(*dd), GFP_KERNEL); |
| 284 | if (dd == NULL) { | 284 | if (!dd) |
| 285 | dev_err(_dev, "no memory for private data\n"); | ||
| 286 | return -ENOMEM; | 285 | return -ENOMEM; |
| 287 | } | ||
| 288 | 286 | ||
| 289 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 287 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
| 290 | if (res == NULL) { | 288 | if (res == NULL) { |
| @@ -2646,6 +2646,7 @@ enum parport_pc_pci_cards { · 2645 unmodified lines | |||
| 2646 | netmos_9901, | 2646 | netmos_9901, |
| 2647 | netmos_9865, | 2647 | netmos_9865, |
| 2648 | quatech_sppxp100, | 2648 | quatech_sppxp100, |
| 2649 | wch_ch382l, | ||
| 2649 | }; | 2650 | }; |
| 2650 | 2651 | ||
| 2651 | 2652 | ||
| @@ -2708,6 +2709,7 @@ static struct parport_pc_pci { · 56 unmodified lines | |||
| 2708 | /* netmos_9901 */ { 1, { { 0, -1 }, } }, | 2709 | /* netmos_9901 */ { 1, { { 0, -1 }, } }, |
| 2709 | /* netmos_9865 */ { 1, { { 0, -1 }, } }, | 2710 | /* netmos_9865 */ { 1, { { 0, -1 }, } }, |
| 2710 | /* quatech_sppxp100 */ { 1, { { 0, 1 }, } }, | 2711 | /* quatech_sppxp100 */ { 1, { { 0, 1 }, } }, |
| 2712 | /* wch_ch382l */ { 1, { { 2, -1 }, } }, | ||
| 2711 | }; | 2713 | }; |
| 2712 | 2714 | ||
| 2713 | static const struct pci_device_id parport_pc_pci_tbl[] = { | 2715 | static const struct pci_device_id parport_pc_pci_tbl[] = { |
| @@ -2797,6 +2799,8 @@ static const struct pci_device_id parport_pc_pci_tbl[] = { · 83 unmodified lines | |||
| 2797 | /* Quatech SPPXP-100 Parallel port PCI ExpressCard */ | 2799 | /* Quatech SPPXP-100 Parallel port PCI ExpressCard */ |
| 2798 | { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_SPPXP_100, | 2800 | { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_SPPXP_100, |
| 2799 | PCI_ANY_ID, PCI_ANY_ID, 0, 0, quatech_sppxp100 }, | 2801 | PCI_ANY_ID, PCI_ANY_ID, 0, 0, quatech_sppxp100 }, |
| 2802 | /* WCH CH382L PCI-E single parallel port card */ | ||
| 2803 | { 0x1c00, 0x3050, 0x1c00, 0x3050, 0, 0, wch_ch382l }, | ||
| 2800 | { 0, } /* terminate list */ | 2804 | { 0, } /* terminate list */ |
| 2801 | }; | 2805 | }; |
| 2802 | MODULE_DEVICE_TABLE(pci, parport_pc_pci_tbl); | 2806 | MODULE_DEVICE_TABLE(pci, parport_pc_pci_tbl); |
| @@ -1,30 +1,24 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0+ | ||
| 1 | /* | 2 | /* |
| 2 | * Support for common PCI multi-I/O cards (which is most of them) | 3 | * Support for common PCI multi-I/O cards (which is most of them) |
| 3 | * | 4 | * |
| 4 | * Copyright (C) 2001 Tim Waugh <twaugh@redhat.com> | 5 | * Copyright (C) 2001 Tim Waugh <twaugh@redhat.com> |
| 5 | * | 6 | * |
| 6 | * This program is free software; you can redistribute it and/or | ||
| 7 | * modify it under the terms of the GNU General Public License | ||
| 8 | * as published by the Free Software Foundation; either version | ||
| 9 | * 2 of the License, or (at your option) any later version. | ||
| 10 | * | ||
| 11 | * | ||
| 12 | * Multi-function PCI cards are supposed to present separate logical | 7 | * Multi-function PCI cards are supposed to present separate logical |
| 13 | * devices on the bus. A common thing to do seems to be to just use | 8 | * devices on the bus. A common thing to do seems to be to just use |
| 14 | * one logical device with lots of base address registers for both | 9 | * one logical device with lots of base address registers for both |
| 15 | * parallel ports and serial ports. This driver is for dealing with | 10 | * parallel ports and serial ports. This driver is for dealing with |
| 16 | * that. | 11 | * that. |
| 17 | * | ||
| 18 | */ | 12 | */ |
| 19 | 13 | ||
| 20 | #include <linux/types.h> | ||
| 21 | #include <linux/module.h> | ||
| 22 | #include <linux/init.h> | ||
| 23 | #include <linux/slab.h> | ||
| 24 | #include <linux/pci.h> | ||
| 25 | #include <linux/interrupt.h> | 14 | #include <linux/interrupt.h> |
| 15 | #include <linux/module.h> | ||
| 26 | #include <linux/parport.h> | 16 | #include <linux/parport.h> |
| 27 | #include <linux/parport_pc.h> | 17 | #include <linux/parport_pc.h> |
| 18 | #include <linux/pci.h> | ||
| 19 | #include <linux/slab.h> | ||
| 20 | #include <linux/types.h> | ||
| 21 | |||
| 28 | #include <linux/8250_pci.h> | 22 | #include <linux/8250_pci.h> |
| 29 | 23 | ||
| 30 | enum parport_pc_pci_cards { | 24 | enum parport_pc_pci_cards { |
| @@ -65,6 +59,7 @@ enum parport_pc_pci_cards { · 34 unmodified lines | |||
| 65 | wch_ch353_1s1p, | 59 | wch_ch353_1s1p, |
| 66 | wch_ch353_2s1p, | 60 | wch_ch353_2s1p, |
| 67 | wch_ch382_2s1p, | 61 | wch_ch382_2s1p, |
| 62 | brainboxes_5s1p, | ||
| 68 | sunix_2s1p, | 63 | sunix_2s1p, |
| 69 | }; | 64 | }; |
| 70 | 65 | ||
| @@ -153,6 +148,7 @@ static struct parport_pc_pci cards[] = { · 82 unmodified lines | |||
| 153 | /* wch_ch353_1s1p*/ { 1, { { 1, -1}, } }, | 148 | /* wch_ch353_1s1p*/ { 1, { { 1, -1}, } }, |
| 154 | /* wch_ch353_2s1p*/ { 1, { { 2, -1}, } }, | 149 | /* wch_ch353_2s1p*/ { 1, { { 2, -1}, } }, |
| 155 | /* wch_ch382_2s1p*/ { 1, { { 2, -1}, } }, | 150 | /* wch_ch382_2s1p*/ { 1, { { 2, -1}, } }, |
| 151 | /* brainboxes_5s1p */ { 1, { { 3, -1 }, } }, | ||
| 156 | /* sunix_2s1p */ { 1, { { 3, -1 }, } }, | 152 | /* sunix_2s1p */ { 1, { { 3, -1 }, } }, |
| 157 | }; | 153 | }; |
| 158 | 154 | ||
| @@ -258,6 +254,10 @@ static struct pci_device_id parport_serial_pci_tbl[] = { · 99 unmodified lines | |||
| 258 | { 0x4348, 0x7053, 0x4348, 0x3253, 0, 0, wch_ch353_2s1p}, | 254 | { 0x4348, 0x7053, 0x4348, 0x3253, 0, 0, wch_ch353_2s1p}, |
| 259 | { 0x1c00, 0x3250, 0x1c00, 0x3250, 0, 0, wch_ch382_2s1p}, | 255 | { 0x1c00, 0x3250, 0x1c00, 0x3250, 0, 0, wch_ch382_2s1p}, |
| 260 | 256 | ||
| 257 | /* BrainBoxes PX272/PX306 MIO card */ | ||
| 258 | { PCI_VENDOR_ID_INTASHIELD, 0x4100, | ||
| 259 | PCI_ANY_ID, PCI_ANY_ID, 0, 0, brainboxes_5s1p }, | ||
| 260 | |||
| 261 | /* | 261 | /* |
| 262 | * More SUNIX variations. At least one of these has part number | 262 | * More SUNIX variations. At least one of these has part number |
| 263 | * '5079A but subdevice 0x102. That board reports 0x0708 as | 263 | * '5079A but subdevice 0x102. That board reports 0x0708 as |
| @@ -501,6 +501,12 @@ static struct pciserial_board pci_parport_serial_boards[] = { · 237 unmodified lines | |||
| 501 | .uart_offset = 8, | 501 | .uart_offset = 8, |
| 502 | .first_offset = 0xC0, | 502 | .first_offset = 0xC0, |
| 503 | }, | 503 | }, |
| 504 | [brainboxes_5s1p] = { | ||
| 505 | .flags = FL_BASE2, | ||
| 506 | .num_ports = 5, | ||
| 507 | .base_baud = 921600, | ||
| 508 | .uart_offset = 8, | ||
| 509 | }, | ||
| 504 | [sunix_2s1p] = { | 510 | [sunix_2s1p] = { |
| 505 | .flags = FL_BASE0|FL_BASE_BARS, | 511 | .flags = FL_BASE0|FL_BASE_BARS, |
| 506 | .num_ports = 2, | 512 | .num_ports = 2, |
| @@ -524,12 +530,10 @@ static int serial_register(struct pci_dev *dev, const struct pci_device_id *id) · 17 unmodified lines | |||
| 524 | struct serial_private *serial; | 530 | struct serial_private *serial; |
| 525 | 531 | ||
| 526 | board = &pci_parport_serial_boards[id->driver_data]; | 532 | board = &pci_parport_serial_boards[id->driver_data]; |
| 527 | |||
| 528 | if (board->num_ports == 0) | 533 | if (board->num_ports == 0) |
| 529 | return 0; | 534 | return 0; |
| 530 | 535 | ||
| 531 | serial = pciserial_init_ports(dev, board); | 536 | serial = pciserial_init_ports(dev, board); |
| 532 | |||
| 533 | if (IS_ERR(serial)) | 537 | if (IS_ERR(serial)) |
| 534 | return PTR_ERR(serial); | 538 | return PTR_ERR(serial); |
| 535 | 539 | ||
| @@ -558,10 +562,9 @@ static int parport_register(struct pci_dev *dev, const struct pci_device_id *id) · 22 unmodified lines | |||
| 558 | int irq; | 562 | int irq; |
| 559 | 563 | ||
| 560 | if (priv->num_par == ARRAY_SIZE (priv->port)) { | 564 | if (priv->num_par == ARRAY_SIZE (priv->port)) { |
| 561 | printk (KERN_WARNING | 565 | dev_warn(&dev->dev, |
| 562 | "parport_serial: %s: only %zu parallel ports " | 566 | "only %zu parallel ports supported (%d reported)\n", |
| 563 | "supported (%d reported)\n", pci_name (dev), | 567 | ARRAY_SIZE(priv->port), card->numports); |
| 564 | ARRAY_SIZE(priv->port), card->numports); | ||
| 565 | break; | 568 | break; |
| 566 | } | 569 | } |
| 567 | 570 | ||
| @@ -577,12 +580,12 @@ static int parport_register(struct pci_dev *dev, const struct pci_device_id *id) · 9 unmodified lines | |||
| 577 | irq = dev->irq; | 580 | irq = dev->irq; |
| 578 | if (irq == IRQ_NONE) { | 581 | if (irq == IRQ_NONE) { |
| 579 | dev_dbg(&dev->dev, | 582 | dev_dbg(&dev->dev, |
| 580 | "PCI parallel port detected: I/O at %#lx(%#lx)\n", | 583 | "PCI parallel port detected: I/O at %#lx(%#lx)\n", |
| 581 | io_lo, io_hi); | 584 | io_lo, io_hi); |
| 582 | irq = PARPORT_IRQ_NONE; | 585 | irq = PARPORT_IRQ_NONE; |
| 583 | } else { | 586 | } else { |
| 584 | dev_dbg(&dev->dev, | 587 | dev_dbg(&dev->dev, |
| 585 | "PCI parallel port detected: I/O at %#lx(%#lx), IRQ %d\n", | 588 | "PCI parallel port detected: I/O at %#lx(%#lx), IRQ %d\n", |
| 586 | io_lo, io_hi, irq); | 589 | io_lo, io_hi, irq); |
| 587 | } | 590 | } |
| 588 | port = parport_pc_probe_port (io_lo, io_hi, irq, | 591 | port = parport_pc_probe_port (io_lo, io_hi, irq, |
| @@ -605,28 +608,26 @@ static int parport_serial_pci_probe(struct pci_dev *dev, · 16 unmodified lines | |||
| 605 | struct parport_serial_private *priv; | 608 | struct parport_serial_private *priv; |
| 606 | int err; | 609 | int err; |
| 607 | 610 | ||
| 608 | priv = kzalloc (sizeof *priv, GFP_KERNEL); | 611 | priv = devm_kzalloc(&dev->dev, sizeof(*priv), GFP_KERNEL); |
| 609 | if (!priv) | 612 | if (!priv) |
| 610 | return -ENOMEM; | 613 | return -ENOMEM; |
| 614 | |||
| 611 | pci_set_drvdata (dev, priv); | 615 | pci_set_drvdata (dev, priv); |
| 612 | 616 | ||
| 613 | err = pci_enable_device (dev); | 617 | err = pcim_enable_device(dev); |
| 614 | if (err) { | 618 | if (err) |
| 615 | kfree (priv); | ||
| 616 | return err; | 619 | return err; |
| 617 | } | ||
| 618 | 620 | ||
| 619 | if (parport_register (dev, id)) { | 621 | err = parport_register(dev, id); |
| 620 | kfree (priv); | 622 | if (err) |
| 621 | return -ENODEV; | 623 | return err; |
| 622 | } | ||
| 623 | 624 | ||
| 624 | if (serial_register (dev, id)) { | 625 | err = serial_register(dev, id); |
| 626 | if (err) { | ||
| 625 | int i; | 627 | int i; |
| 626 | for (i = 0; i < priv->num_par; i++) | 628 | for (i = 0; i < priv->num_par; i++) |
| 627 | parport_pc_unregister_port (priv->port[i]); | 629 | parport_pc_unregister_port (priv->port[i]); |
| 628 | kfree (priv); | 630 | return err; |
| 629 | return -ENODEV; | ||
| 630 | } | 631 | } |
| 631 | 632 | ||
| 632 | return 0; | 633 | return 0; |
| @@ -645,78 +646,47 @@ static void parport_serial_pci_remove(struct pci_dev *dev) · 12 unmodified lines | |||
| 645 | for (i = 0; i < priv->num_par; i++) | 646 | for (i = 0; i < priv->num_par; i++) |
| 646 | parport_pc_unregister_port (priv->port[i]); | 647 | parport_pc_unregister_port (priv->port[i]); |
| 647 | 648 | ||
| 648 | kfree (priv); | ||
| 649 | return; | 649 | return; |
| 650 | } | 650 | } |
| 651 | 651 | ||
| 652 | #ifdef CONFIG_PM | 652 | static int __maybe_unused parport_serial_pci_suspend(struct device *dev) |
| 653 | static int parport_serial_pci_suspend(struct pci_dev *dev, pm_message_t state) | ||
| 654 | { | 653 | { |
| 655 | struct parport_serial_private *priv = pci_get_drvdata(dev); | 654 | struct pci_dev *pdev = to_pci_dev(dev); |
| 655 | struct parport_serial_private *priv = pci_get_drvdata(pdev); | ||
| 656 | 656 | ||
| 657 | if (priv->serial) | 657 | if (priv->serial) |
| 658 | pciserial_suspend_ports(priv->serial); | 658 | pciserial_suspend_ports(priv->serial); |
| 659 | 659 | ||
| 660 | /* FIXME: What about parport? */ | 660 | /* FIXME: What about parport? */ |
| 661 | |||
| 662 | pci_save_state(dev); | ||
| 663 | pci_set_power_state(dev, pci_choose_state(dev, state)); | ||
| 664 | return 0; | 661 | return 0; |
| 665 | } | 662 | } |
| 666 | 663 | ||
| 667 | static int parport_serial_pci_resume(struct pci_dev *dev) | 664 | static int __maybe_unused parport_serial_pci_resume(struct device *dev) |
| 668 | { | 665 | { |
| 669 | struct parport_serial_private *priv = pci_get_drvdata(dev); | 666 | struct pci_dev *pdev = to_pci_dev(dev); |
| 670 | int err; | 667 | struct parport_serial_private *priv = pci_get_drvdata(pdev); |
| 671 | |||
| 672 | pci_set_power_state(dev, PCI_D0); | ||
| 673 | pci_restore_state(dev); | ||
| 674 | |||
| 675 | /* | ||
| 676 | * The device may have been disabled. Re-enable it. | ||
| 677 | */ | ||
| 678 | err = pci_enable_device(dev); | ||
| 679 | if (err) { | ||
| 680 | printk(KERN_ERR "parport_serial: %s: error enabling " | ||
| 681 | "device for resume (%d)\n", pci_name(dev), err); | ||
| 682 | return err; | ||
| 683 | } | ||
| 684 | 668 | ||
| 685 | if (priv->serial) | 669 | if (priv->serial) |
| 686 | pciserial_resume_ports(priv->serial); | 670 | pciserial_resume_ports(priv->serial); |
| 687 | 671 | ||
| 688 | /* FIXME: What about parport? */ | 672 | /* FIXME: What about parport? */ |
| 689 | |||
| 690 | return 0; | 673 | return 0; |
| 691 | } | 674 | } |
| 692 | #endif | 675 | |
| 676 | static SIMPLE_DEV_PM_OPS(parport_serial_pm_ops, | ||
| 677 | parport_serial_pci_suspend, parport_serial_pci_resume); | ||
| 693 | 678 | ||
| 694 | static struct pci_driver parport_serial_pci_driver = { | 679 | static struct pci_driver parport_serial_pci_driver = { |
| 695 | .name = "parport_serial", | 680 | .name = "parport_serial", |
| 696 | .id_table = parport_serial_pci_tbl, | 681 | .id_table = parport_serial_pci_tbl, |
| 697 | .probe = parport_serial_pci_probe, | 682 | .probe = parport_serial_pci_probe, |
| 698 | .remove = parport_serial_pci_remove, | 683 | .remove = parport_serial_pci_remove, |
| 699 | #ifdef CONFIG_PM | 684 | .driver = { |
| 700 | .suspend = parport_serial_pci_suspend, | 685 | .pm = &parport_serial_pm_ops, |
| 701 | .resume = parport_serial_pci_resume, | 686 | }, |
| 702 | #endif | ||
| 703 | }; | 687 | }; |
| 704 | 688 | module_pci_driver(parport_serial_pci_driver); | |
| 705 | |||
| 706 | static int __init parport_serial_init (void) | ||
| 707 | { | ||
| 708 | return pci_register_driver (&parport_serial_pci_driver); | ||
| 709 | } | ||
| 710 | |||
| 711 | static void __exit parport_serial_exit (void) | ||
| 712 | { | ||
| 713 | pci_unregister_driver (&parport_serial_pci_driver); | ||
| 714 | return; | ||
| 715 | } | ||
| 716 | 689 | ||
| 717 | MODULE_AUTHOR("Tim Waugh <twaugh@redhat.com>"); | 690 | MODULE_AUTHOR("Tim Waugh <twaugh@redhat.com>"); |
| 718 | MODULE_DESCRIPTION("Driver for common parallel+serial multi-I/O PCI cards"); | 691 | MODULE_DESCRIPTION("Driver for common parallel+serial multi-I/O PCI cards"); |
| 719 | MODULE_LICENSE("GPL"); | 692 | MODULE_LICENSE("GPL"); |
| 720 | |||
| 721 | module_init(parport_serial_init); | ||
| 722 | module_exit(parport_serial_exit); | ||
| @@ -49,6 +49,7 @@ MODULE_PARM_DESC(clear_wait, · 48 unmodified lines | |||
| 49 | " zero turns clear edge capture off entirely"); | 49 | " zero turns clear edge capture off entirely"); |
| 50 | module_param(clear_wait, uint, 0); | 50 | module_param(clear_wait, uint, 0); |
| 51 | 51 | ||
| 52 | static DEFINE_IDA(pps_client_index); | ||
| 52 | 53 | ||
| 53 | /* internal per port structure */ | 54 | /* internal per port structure */ |
| 54 | struct pps_client_pp { | 55 | struct pps_client_pp { |
| @@ -56,6 +57,7 @@ struct pps_client_pp { · 1 unmodified line | |||
| 56 | struct pps_device *pps; /* PPS device */ | 57 | struct pps_device *pps; /* PPS device */ |
| 57 | unsigned int cw; /* port clear timeout */ | 58 | unsigned int cw; /* port clear timeout */ |
| 58 | unsigned int cw_err; /* number of timeouts */ | 59 | unsigned int cw_err; /* number of timeouts */ |
| 60 | int index; /* device number */ | ||
| 59 | }; | 61 | }; |
| 60 | 62 | ||
| 61 | static inline int signal_is_set(struct parport *port) | 63 | static inline int signal_is_set(struct parport *port) |
| @@ -136,6 +138,8 @@ out_both: · 74 unmodified lines | |||
| 136 | 138 | ||
| 137 | static void parport_attach(struct parport *port) | 139 | static void parport_attach(struct parport *port) |
| 138 | { | 140 | { |
| 141 | struct pardev_cb pps_client_cb; | ||
| 142 | int index; | ||
| 139 | struct pps_client_pp *device; | 143 | struct pps_client_pp *device; |
| 140 | struct pps_source_info info = { | 144 | struct pps_source_info info = { |
| 141 | .name = KBUILD_MODNAME, | 145 | .name = KBUILD_MODNAME, |
| @@ -154,8 +158,15 @@ static void parport_attach(struct parport *port) · 12 unmodified lines | |||
| 154 | return; | 158 | return; |
| 155 | } | 159 | } |
| 156 | 160 | ||
| 157 | device->pardev = parport_register_device(port, KBUILD_MODNAME, | 161 | index = ida_simple_get(&pps_client_index, 0, 0, GFP_KERNEL); |
| 158 | NULL, NULL, parport_irq, PARPORT_FLAG_EXCL, device); | 162 | memset(&pps_client_cb, 0, sizeof(pps_client_cb)); |
| 163 | pps_client_cb.private = device; | ||
| 164 | pps_client_cb.irq_func = parport_irq; | ||
| 165 | pps_client_cb.flags = PARPORT_FLAG_EXCL; | ||
| 166 | device->pardev = parport_register_dev_model(port, | ||
| 167 | KBUILD_MODNAME, | ||
| 168 | &pps_client_cb, | ||
| 169 | index); | ||
| 159 | if (!device->pardev) { | 170 | if (!device->pardev) { |
| 160 | pr_err("couldn't register with %s\n", port->name); | 171 | pr_err("couldn't register with %s\n", port->name); |
| 161 | goto err_free; | 172 | goto err_free; |
| @@ -176,6 +187,7 @@ static void parport_attach(struct parport *port) · 14 unmodified lines | |||
| 176 | device->cw = clear_wait; | 187 | device->cw = clear_wait; |
| 177 | 188 | ||
| 178 | port->ops->enable_irq(port); | 189 | port->ops->enable_irq(port); |
| 190 | device->index = index; | ||
| 179 | 191 | ||
| 180 | pr_info("attached to %s\n", port->name); | 192 | pr_info("attached to %s\n", port->name); |
| 181 | 193 | ||
| @@ -186,6 +198,7 @@ err_release_dev: · 4 unmodified lines | |||
| 186 | err_unregister_dev: | 198 | err_unregister_dev: |
| 187 | parport_unregister_device(device->pardev); | 199 | parport_unregister_device(device->pardev); |
| 188 | err_free: | 200 | err_free: |
| 201 | ida_simple_remove(&pps_client_index, index); | ||
| 189 | kfree(device); | 202 | kfree(device); |
| 190 | } | 203 | } |
| 191 | 204 | ||
| @@ -205,13 +218,15 @@ static void parport_detach(struct parport *port) · 13 unmodified lines | |||
| 205 | pps_unregister_source(device->pps); | 218 | pps_unregister_source(device->pps); |
| 206 | parport_release(pardev); | 219 | parport_release(pardev); |
| 207 | parport_unregister_device(pardev); | 220 | parport_unregister_device(pardev); |
| 221 | ida_simple_remove(&pps_client_index, device->index); | ||
| 208 | kfree(device); | 222 | kfree(device); |
| 209 | } | 223 | } |
| 210 | 224 | ||
| 211 | static struct parport_driver pps_parport_driver = { | 225 | static struct parport_driver pps_parport_driver = { |
| 212 | .name = KBUILD_MODNAME, | 226 | .name = KBUILD_MODNAME, |
| 213 | .attach = parport_attach, | 227 | .match_port = parport_attach, |
| 214 | .detach = parport_detach, | 228 | .detach = parport_detach, |
| 229 | .devmodel = true, | ||
| 215 | }; | 230 | }; |
| 216 | 231 | ||
| 217 | /* module staff */ | 232 | /* module staff */ |
| @@ -192,13 +192,18 @@ static inline ktime_t next_intr_time(struct pps_generator_pp *dev) · 191 unmodified lines | |||
| 192 | 192 | ||
| 193 | static void parport_attach(struct parport *port) | 193 | static void parport_attach(struct parport *port) |
| 194 | { | 194 | { |
| 195 | struct pardev_cb pps_cb; | ||
| 196 | |||
| 195 | if (attached) { | 197 | if (attached) { |
| 196 | /* we already have a port */ | 198 | /* we already have a port */ |
| 197 | return; | 199 | return; |
| 198 | } | 200 | } |
| 199 | 201 | ||
| 200 | device.pardev = parport_register_device(port, KBUILD_MODNAME, | 202 | memset(&pps_cb, 0, sizeof(pps_cb)); |
| 201 | NULL, NULL, NULL, PARPORT_FLAG_EXCL, &device); | 203 | pps_cb.private = &device; |
| 204 | pps_cb.flags = PARPORT_FLAG_EXCL; | ||
| 205 | device.pardev = parport_register_dev_model(port, KBUILD_MODNAME, | ||
| 206 | &pps_cb, 0); | ||
| 202 | if (!device.pardev) { | 207 | if (!device.pardev) { |
| 203 | pr_err("couldn't register with %s\n", port->name); | 208 | pr_err("couldn't register with %s\n", port->name); |
| 204 | return; | 209 | return; |
| @@ -236,8 +241,9 @@ static void parport_detach(struct parport *port) · 31 unmodified lines | |||
| 236 | 241 | ||
| 237 | static struct parport_driver pps_gen_parport_driver = { | 242 | static struct parport_driver pps_gen_parport_driver = { |
| 238 | .name = KBUILD_MODNAME, | 243 | .name = KBUILD_MODNAME, |
| 239 | .attach = parport_attach, | 244 | .match_port = parport_attach, |
| 240 | .detach = parport_detach, | 245 | .detach = parport_detach, |
| 246 | .devmodel = true, | ||
| 241 | }; | 247 | }; |
| 242 | 248 | ||
| 243 | /* module staff */ | 249 | /* module staff */ |
| @@ -594,7 +594,7 @@ static ssize_t device_add_store(struct device *dev, · 593 unmodified lines | |||
| 594 | size_t inbytes = 0, outbytes = 0; | 594 | size_t inbytes = 0, outbytes = 0; |
| 595 | u8 statustype = 0; | 595 | u8 statustype = 0; |
| 596 | 596 | ||
| 597 | ret = sscanf(buf, "%20s %zu %zu %hhu", type, &inbytes, | 597 | ret = sscanf(buf, "%19s %zu %zu %hhu", type, &inbytes, |
| 598 | &outbytes, &statustype); | 598 | &outbytes, &statustype); |
| 599 | if (ret != 3 && ret != 4) | 599 | if (ret != 3 && ret != 4) |
| 600 | return -EINVAL; | 600 | return -EINVAL; |
| @@ -141,7 +141,7 @@ static struct slim_device *slim_alloc_device(struct slim_controller *ctrl, · 140 unmodified lines | |||
| 141 | sbdev->e_addr = *eaddr; | 141 | sbdev->e_addr = *eaddr; |
| 142 | ret = slim_add_device(ctrl, sbdev, node); | 142 | ret = slim_add_device(ctrl, sbdev, node); |
| 143 | if (ret) { | 143 | if (ret) { |
| 144 | kfree(sbdev); | 144 | put_device(&sbdev->dev); |
| 145 | return NULL; | 145 | return NULL; |
| 146 | } | 146 | } |
| 147 | 147 | ||
| @@ -170,24 +170,22 @@ static int dma_port_write(struct tb_ctl *ctl, const void *buffer, u64 route, · 169 unmodified lines | |||
| 170 | 170 | ||
| 171 | static int dma_find_port(struct tb_switch *sw) | 171 | static int dma_find_port(struct tb_switch *sw) |
| 172 | { | 172 | { |
| 173 | int port, ret; | 173 | static const int ports[] = { 3, 5, 7 }; |
| 174 | u32 type; | 174 | int i; |
| 175 | 175 | ||
| 176 | /* | 176 | /* |
| 177 | * The DMA (NHI) port is either 3 or 5 depending on the | 177 | * The DMA (NHI) port is either 3, 5 or 7 depending on the |
| 178 | * controller. Try both starting from 5 which is more common. | 178 | * controller. Try all of them. |
| 179 | */ | 179 | */ |
| 180 | port = 5; | 180 | for (i = 0; i < ARRAY_SIZE(ports); i++) { |
| 181 | ret = dma_port_read(sw->tb->ctl, &type, tb_route(sw), port, 2, 1, | 181 | u32 type; |
| 182 | DMA_PORT_TIMEOUT); | 182 | int ret; |
| 183 | if (!ret && (type & 0xffffff) == TB_TYPE_NHI) | 183 | |
| 184 | return port; | 184 | ret = dma_port_read(sw->tb->ctl, &type, tb_route(sw), ports[i], |
| 185 | 185 | 2, 1, DMA_PORT_TIMEOUT); | |
| 186 | port = 3; | 186 | if (!ret && (type & 0xffffff) == TB_TYPE_NHI) |
| 187 | ret = dma_port_read(sw->tb->ctl, &type, tb_route(sw), port, 2, 1, | 187 | return ports[i]; |
| 188 | DMA_PORT_TIMEOUT); | 188 | } |
| 189 | if (!ret && (type & 0xffffff) == TB_TYPE_NHI) | ||
| 190 | return port; | ||
| 191 | 189 | ||
| 192 | return -ENODEV; | 190 | return -ENODEV; |
| 193 | } | 191 | } |
| @@ -117,23 +117,151 @@ static const char * const tb_security_names[] = { · 116 unmodified lines | |||
| 117 | [TB_SECURITY_USER] = "user", | 117 | [TB_SECURITY_USER] = "user", |
| 118 | [TB_SECURITY_SECURE] = "secure", | 118 | [TB_SECURITY_SECURE] = "secure", |
| 119 | [TB_SECURITY_DPONLY] = "dponly", | 119 | [TB_SECURITY_DPONLY] = "dponly", |
| 120 | [TB_SECURITY_USBONLY] = "usbonly", | ||
| 120 | }; | 121 | }; |
| 121 | 122 | ||
| 123 | static ssize_t boot_acl_show(struct device *dev, struct device_attribute *attr, | ||
| 124 | char *buf) | ||
| 125 | { | ||
| 126 | struct tb *tb = container_of(dev, struct tb, dev); | ||
| 127 | uuid_t *uuids; | ||
| 128 | ssize_t ret; | ||
| 129 | int i; | ||
| 130 | |||
| 131 | uuids = kcalloc(tb->nboot_acl, sizeof(uuid_t), GFP_KERNEL); | ||
| 132 | if (!uuids) | ||
| 133 | return -ENOMEM; | ||
| 134 | |||
| 135 | if (mutex_lock_interruptible(&tb->lock)) { | ||
| 136 | ret = -ERESTARTSYS; | ||
| 137 | goto out; | ||
| 138 | } | ||
| 139 | ret = tb->cm_ops->get_boot_acl(tb, uuids, tb->nboot_acl); | ||
| 140 | if (ret) { | ||
| 141 | mutex_unlock(&tb->lock); | ||
| 142 | goto out; | ||
| 143 | } | ||
| 144 | mutex_unlock(&tb->lock); | ||
| 145 | |||
| 146 | for (ret = 0, i = 0; i < tb->nboot_acl; i++) { | ||
| 147 | if (!uuid_is_null(&uuids[i])) | ||
| 148 | ret += snprintf(buf + ret, PAGE_SIZE - ret, "%pUb", | ||
| 149 | &uuids[i]); | ||
| 150 | |||
| 151 | ret += snprintf(buf + ret, PAGE_SIZE - ret, "%s", | ||
| 152 | i < tb->nboot_acl - 1 ? "," : "\n"); | ||
| 153 | } | ||
| 154 | |||
| 155 | out: | ||
| 156 | kfree(uuids); | ||
| 157 | return ret; | ||
| 158 | } | ||
| 159 | |||
| 160 | static ssize_t boot_acl_store(struct device *dev, struct device_attribute *attr, | ||
| 161 | const char *buf, size_t count) | ||
| 162 | { | ||
| 163 | struct tb *tb = container_of(dev, struct tb, dev); | ||
| 164 | char *str, *s, *uuid_str; | ||
| 165 | ssize_t ret = 0; | ||
| 166 | uuid_t *acl; | ||
| 167 | int i = 0; | ||
| 168 | |||
| 169 | /* | ||
| 170 | * Make sure the value is not bigger than tb->nboot_acl * UUID | ||
| 171 | * length + commas and optional "\n". Also the smallest allowable | ||
| 172 | * string is tb->nboot_acl * ",". | ||
| 173 | */ | ||
| 174 | if (count > (UUID_STRING_LEN + 1) * tb->nboot_acl + 1) | ||
| 175 | return -EINVAL; | ||
| 176 | if (count < tb->nboot_acl - 1) | ||
| 177 | return -EINVAL; | ||
| 178 | |||
| 179 | str = kstrdup(buf, GFP_KERNEL); | ||
| 180 | if (!str) | ||
| 181 | return -ENOMEM; | ||
| 182 | |||
| 183 | acl = kcalloc(tb->nboot_acl, sizeof(uuid_t), GFP_KERNEL); | ||
| 184 | if (!acl) { | ||
| 185 | ret = -ENOMEM; | ||
| 186 | goto err_free_str; | ||
| 187 | } | ||
| 188 | |||
| 189 | uuid_str = strim(str); | ||
| 190 | while ((s = strsep(&uuid_str, ",")) != NULL && i < tb->nboot_acl) { | ||
| 191 | size_t len = strlen(s); | ||
| 192 | |||
| 193 | if (len) { | ||
| 194 | if (len != UUID_STRING_LEN) { | ||
| 195 | ret = -EINVAL; | ||
| 196 | goto err_free_acl; | ||
| 197 | } | ||
| 198 | ret = uuid_parse(s, &acl[i]); | ||
| 199 | if (ret) | ||
| 200 | goto err_free_acl; | ||
| 201 | } | ||
| 202 | |||
| 203 | i++; | ||
| 204 | } | ||
| 205 | |||
| 206 | if (s || i < tb->nboot_acl) { | ||
| 207 | ret = -EINVAL; | ||
| 208 | goto err_free_acl; | ||
| 209 | } | ||
| 210 | |||
| 211 | if (mutex_lock_interruptible(&tb->lock)) { | ||
| 212 | ret = -ERESTARTSYS; | ||
| 213 | goto err_free_acl; | ||
| 214 | } | ||
| 215 | ret = tb->cm_ops->set_boot_acl(tb, acl, tb->nboot_acl); | ||
| 216 | mutex_unlock(&tb->lock); | ||
| 217 | |||
| 218 | err_free_acl: | ||
| 219 | kfree(acl); | ||
| 220 | err_free_str: | ||
| 221 | kfree(str); | ||
| 222 | |||
| 223 | return ret ?: count; | ||
| 224 | } | ||
| 225 | static DEVICE_ATTR_RW(boot_acl); | ||
| 226 | |||
| 122 | static ssize_t security_show(struct device *dev, struct device_attribute *attr, | 227 | static ssize_t security_show(struct device *dev, struct device_attribute *attr, |
| 123 | char *buf) | 228 | char *buf) |
| 124 | { | 229 | { |
| 125 | struct tb *tb = container_of(dev, struct tb, dev); | 230 | struct tb *tb = container_of(dev, struct tb, dev); |
| 231 | const char *name = "unknown"; | ||
| 126 | 232 | ||
| 127 | return sprintf(buf, "%s\n", tb_security_names[tb->security_level]); | 233 | if (tb->security_level < ARRAY_SIZE(tb_security_names)) |
| 234 | name = tb_security_names[tb->security_level]; | ||
| 235 | |||
| 236 | return sprintf(buf, "%s\n", name); | ||
| 128 | } | 237 | } |
| 129 | static DEVICE_ATTR_RO(security); | 238 | static DEVICE_ATTR_RO(security); |
| 130 | 239 | ||
| 131 | static struct attribute *domain_attrs[] = { | 240 | static struct attribute *domain_attrs[] = { |
| 241 | &dev_attr_boot_acl.attr, | ||
| 132 | &dev_attr_security.attr, | 242 | &dev_attr_security.attr, |
| 133 | NULL, | 243 | NULL, |
| 134 | }; | 244 | }; |
| 135 | 245 | ||
| 246 | static umode_t domain_attr_is_visible(struct kobject *kobj, | ||
| 247 | struct attribute *attr, int n) | ||
| 248 | { | ||
| 249 | struct device *dev = container_of(kobj, struct device, kobj); | ||
| 250 | struct tb *tb = container_of(dev, struct tb, dev); | ||
| 251 | |||
| 252 | if (attr == &dev_attr_boot_acl.attr) { | ||
| 253 | if (tb->nboot_acl && | ||
| 254 | tb->cm_ops->get_boot_acl && | ||
| 255 | tb->cm_ops->set_boot_acl) | ||
| 256 | return attr->mode; | ||
| 257 | return 0; | ||
| 258 | } | ||
| 259 | |||
| 260 | return attr->mode; | ||
| 261 | } | ||
| 262 | |||
| 136 | static struct attribute_group domain_attr_group = { | 263 | static struct attribute_group domain_attr_group = { |
| 264 | .is_visible = domain_attr_is_visible, | ||
| 137 | .attrs = domain_attrs, | 265 | .attrs = domain_attrs, |
| 138 | }; | 266 | }; |
| 139 | 267 | ||
| @@ -41,7 +41,8 @@ · 40 unmodified lines | |||
| 41 | #define PHY_PORT_CS1_LINK_STATE_MASK GENMASK(29, 26) | 41 | #define PHY_PORT_CS1_LINK_STATE_MASK GENMASK(29, 26) |
| 42 | #define PHY_PORT_CS1_LINK_STATE_SHIFT 26 | 42 | #define PHY_PORT_CS1_LINK_STATE_SHIFT 26 |
| 43 | 43 | ||
| 44 | #define ICM_TIMEOUT 5000 /* ms */ | 44 | #define ICM_TIMEOUT 5000 /* ms */ |
| 45 | #define ICM_APPROVE_TIMEOUT 10000 /* ms */ | ||
| 45 | #define ICM_MAX_LINK 4 | 46 | #define ICM_MAX_LINK 4 |
| 46 | #define ICM_MAX_DEPTH 6 | 47 | #define ICM_MAX_DEPTH 6 |
| 47 | 48 | ||
| @@ -55,9 +56,11 @@ · 7 unmodified lines | |||
| 55 | * @vnd_cap: Vendor defined capability where PCIe2CIO mailbox resides | 56 | * @vnd_cap: Vendor defined capability where PCIe2CIO mailbox resides |
| 56 | * (only set when @upstream_port is not %NULL) | 57 | * (only set when @upstream_port is not %NULL) |
| 57 | * @safe_mode: ICM is in safe mode | 58 | * @safe_mode: ICM is in safe mode |
| 59 | * @max_boot_acl: Maximum number of preboot ACL entries (%0 if not supported) | ||
| 58 | * @is_supported: Checks if we can support ICM on this controller | 60 | * @is_supported: Checks if we can support ICM on this controller |
| 59 | * @get_mode: Read and return the ICM firmware mode (optional) | 61 | * @get_mode: Read and return the ICM firmware mode (optional) |
| 60 | * @get_route: Find a route string for given switch | 62 | * @get_route: Find a route string for given switch |
| 63 | * @driver_ready: Send driver ready message to ICM | ||
| 61 | * @device_connected: Handle device connected ICM message | 64 | * @device_connected: Handle device connected ICM message |
| 62 | * @device_disconnected: Handle device disconnected ICM message | 65 | * @device_disconnected: Handle device disconnected ICM message |
| 63 | * @xdomain_connected - Handle XDomain connected ICM message | 66 | * @xdomain_connected - Handle XDomain connected ICM message |
| @@ -67,11 +70,15 @@ struct icm { · 3 unmodified lines | |||
| 67 | struct mutex request_lock; | 70 | struct mutex request_lock; |
| 68 | struct delayed_work rescan_work; | 71 | struct delayed_work rescan_work; |
| 69 | struct pci_dev *upstream_port; | 72 | struct pci_dev *upstream_port; |
| 73 | size_t max_boot_acl; | ||
| 70 | int vnd_cap; | 74 | int vnd_cap; |
| 71 | bool safe_mode; | 75 | bool safe_mode; |
| 72 | bool (*is_supported)(struct tb *tb); | 76 | bool (*is_supported)(struct tb *tb); |
| 73 | int (*get_mode)(struct tb *tb); | 77 | int (*get_mode)(struct tb *tb); |
| 74 | int (*get_route)(struct tb *tb, u8 link, u8 depth, u64 *route); | 78 | int (*get_route)(struct tb *tb, u8 link, u8 depth, u64 *route); |
| 79 | int (*driver_ready)(struct tb *tb, | ||
| 80 | enum tb_security_level *security_level, | ||
| 81 | size_t *nboot_acl); | ||
| 75 | void (*device_connected)(struct tb *tb, | 82 | void (*device_connected)(struct tb *tb, |
| 76 | const struct icm_pkg_header *hdr); | 83 | const struct icm_pkg_header *hdr); |
| 77 | void (*device_disconnected)(struct tb *tb, | 84 | void (*device_disconnected)(struct tb *tb, |
| @@ -111,6 +118,12 @@ static inline u64 get_route(u32 route_hi, u32 route_lo) · 33 unmodified lines | |||
| 111 | return (u64)route_hi << 32 | route_lo; | 118 | return (u64)route_hi << 32 | route_lo; |
| 112 | } | 119 | } |
| 113 | 120 | ||
| 121 | static inline u64 get_parent_route(u64 route) | ||
| 122 | { | ||
| 123 | int depth = tb_route_length(route); | ||
| 124 | return depth ? route & ~(0xffULL << (depth - 1) * TB_ROUTE_SHIFT) : 0; | ||
| 125 | } | ||
| 126 | |||
| 114 | static bool icm_match(const struct tb_cfg_request *req, | 127 | static bool icm_match(const struct tb_cfg_request *req, |
| 115 | const struct ctl_pkg *pkg) | 128 | const struct ctl_pkg *pkg) |
| 116 | { | 129 | { |
| @@ -245,6 +258,28 @@ err_free: · 128 unmodified lines | |||
| 245 | return ret; | 258 | return ret; |
| 246 | } | 259 | } |
| 247 | 260 | ||
| 261 | static int | ||
| 262 | icm_fr_driver_ready(struct tb *tb, enum tb_security_level *security_level, | ||
| 263 | size_t *nboot_acl) | ||
| 264 | { | ||
| 265 | struct icm_fr_pkg_driver_ready_response reply; | ||
| 266 | struct icm_pkg_driver_ready request = { | ||
| 267 | .hdr.code = ICM_DRIVER_READY, | ||
| 268 | }; | ||
| 269 | int ret; | ||
| 270 | |||
| 271 | memset(&reply, 0, sizeof(reply)); | ||
| 272 | ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply), | ||
| 273 | 1, ICM_TIMEOUT); | ||
| 274 | if (ret) | ||
| 275 | return ret; | ||
| 276 | |||
| 277 | if (security_level) | ||
| 278 | *security_level = reply.security_level & ICM_FR_SLEVEL_MASK; | ||
| 279 | |||
| 280 | return 0; | ||
| 281 | } | ||
| 282 | |||
| 248 | static int icm_fr_approve_switch(struct tb *tb, struct tb_switch *sw) | 283 | static int icm_fr_approve_switch(struct tb *tb, struct tb_switch *sw) |
| 249 | { | 284 | { |
| 250 | struct icm_fr_pkg_approve_device request; | 285 | struct icm_fr_pkg_approve_device request; |
| @@ -260,7 +295,7 @@ static int icm_fr_approve_switch(struct tb *tb, struct tb_switch *sw) · 9 unmodified lines | |||
| 260 | memset(&reply, 0, sizeof(reply)); | 295 | memset(&reply, 0, sizeof(reply)); |
| 261 | /* Use larger timeout as establishing tunnels can take some time */ | 296 | /* Use larger timeout as establishing tunnels can take some time */ |
| 262 | ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply), | 297 | ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply), |
| 263 | 1, 10000); | 298 | 1, ICM_APPROVE_TIMEOUT); |
| 264 | if (ret) | 299 | if (ret) |
| 265 | return ret; | 300 | return ret; |
| 266 | 301 | ||
| @@ -374,6 +409,59 @@ static int icm_fr_disconnect_xdomain_paths(struct tb *tb, struct tb_xdomain *xd) · 107 unmodified lines | |||
| 374 | return 0; | 409 | return 0; |
| 375 | } | 410 | } |
| 376 | 411 | ||
| 412 | static void add_switch(struct tb_switch *parent_sw, u64 route, | ||
| 413 | const uuid_t *uuid, u8 connection_id, u8 connection_key, | ||
| 414 | u8 link, u8 depth, enum tb_security_level security_level, | ||
| 415 | bool authorized, bool boot) | ||
| 416 | { | ||
| 417 | struct tb_switch *sw; | ||
| 418 | |||
| 419 | sw = tb_switch_alloc(parent_sw->tb, &parent_sw->dev, route); | ||
| 420 | if (!sw) | ||
| 421 | return; | ||
| 422 | |||
| 423 | sw->uuid = kmemdup(uuid, sizeof(*uuid), GFP_KERNEL); | ||
| 424 | sw->connection_id = connection_id; | ||
| 425 | sw->connection_key = connection_key; | ||
| 426 | sw->link = link; | ||
| 427 | sw->depth = depth; | ||
| 428 | sw->authorized = authorized; | ||
| 429 | sw->security_level = security_level; | ||
| 430 | sw->boot = boot; | ||
| 431 | |||
| 432 | /* Link the two switches now */ | ||
| 433 | tb_port_at(route, parent_sw)->remote = tb_upstream_port(sw); | ||
| 434 | tb_upstream_port(sw)->remote = tb_port_at(route, parent_sw); | ||
| 435 | |||
| 436 | if (tb_switch_add(sw)) { | ||
| 437 | tb_port_at(tb_route(sw), parent_sw)->remote = NULL; | ||
| 438 | tb_switch_put(sw); | ||
| 439 | return; | ||
| 440 | } | ||
| 441 | } | ||
| 442 | |||
| 443 | static void update_switch(struct tb_switch *parent_sw, struct tb_switch *sw, | ||
| 444 | u64 route, u8 connection_id, u8 connection_key, | ||
| 445 | u8 link, u8 depth, bool boot) | ||
| 446 | { | ||
| 447 | /* Disconnect from parent */ | ||
| 448 | tb_port_at(tb_route(sw), parent_sw)->remote = NULL; | ||
| 449 | /* Re-connect via updated port*/ | ||
| 450 | tb_port_at(route, parent_sw)->remote = tb_upstream_port(sw); | ||
| 451 | |||
| 452 | /* Update with the new addressing information */ | ||
| 453 | sw->config.route_hi = upper_32_bits(route); | ||
| 454 | sw->config.route_lo = lower_32_bits(route); | ||
| 455 | sw->connection_id = connection_id; | ||
| 456 | sw->connection_key = connection_key; | ||
| 457 | sw->link = link; | ||
| 458 | sw->depth = depth; | ||
| 459 | sw->boot = boot; | ||
| 460 | |||
| 461 | /* This switch still exists */ | ||
| 462 | sw->is_unplugged = false; | ||
| 463 | } | ||
| 464 | |||
| 377 | static void remove_switch(struct tb_switch *sw) | 465 | static void remove_switch(struct tb_switch *sw) |
| 378 | { | 466 | { |
| 379 | struct tb_switch *parent_sw; | 467 | struct tb_switch *parent_sw; |
| @@ -383,15 +471,52 @@ static void remove_switch(struct tb_switch *sw) · 3 unmodified lines | |||
| 383 | tb_switch_remove(sw); | 471 | tb_switch_remove(sw); |
| 384 | } | 472 | } |
| 385 | 473 | ||
| 474 | static void add_xdomain(struct tb_switch *sw, u64 route, | ||
| 475 | const uuid_t *local_uuid, const uuid_t *remote_uuid, | ||
| 476 | u8 link, u8 depth) | ||
| 477 | { | ||
| 478 | struct tb_xdomain *xd; | ||
| 479 | |||
| 480 | xd = tb_xdomain_alloc(sw->tb, &sw->dev, route, local_uuid, remote_uuid); | ||
| 481 | if (!xd) | ||
| 482 | return; | ||
| 483 | |||
| 484 | xd->link = link; | ||
| 485 | xd->depth = depth; | ||
| 486 | |||
| 487 | tb_port_at(route, sw)->xdomain = xd; | ||
| 488 | |||
| 489 | tb_xdomain_add(xd); | ||
| 490 | } | ||
| 491 | |||
| 492 | static void update_xdomain(struct tb_xdomain *xd, u64 route, u8 link) | ||
| 493 | { | ||
| 494 | xd->link = link; | ||
| 495 | xd->route = route; | ||
| 496 | xd->is_unplugged = false; | ||
| 497 | } | ||
| 498 | |||
| 499 | static void remove_xdomain(struct tb_xdomain *xd) | ||
| 500 | { | ||
| 501 | struct tb_switch *sw; | ||
| 502 | |||
| 503 | sw = tb_to_switch(xd->dev.parent); | ||
| 504 | tb_port_at(xd->route, sw)->xdomain = NULL; | ||
| 505 | tb_xdomain_remove(xd); | ||
| 506 | } | ||
| 507 | |||
| 386 | static void | 508 | static void |
| 387 | icm_fr_device_connected(struct tb *tb, const struct icm_pkg_header *hdr) | 509 | icm_fr_device_connected(struct tb *tb, const struct icm_pkg_header *hdr) |
| 388 | { | 510 | { |
| 389 | const struct icm_fr_event_device_connected *pkg = | 511 | const struct icm_fr_event_device_connected *pkg = |
| 390 | (const struct icm_fr_event_device_connected *)hdr; | 512 | (const struct icm_fr_event_device_connected *)hdr; |
| 513 | enum tb_security_level security_level; | ||
| 391 | struct tb_switch *sw, *parent_sw; | 514 | struct tb_switch *sw, *parent_sw; |
| 392 | struct icm *icm = tb_priv(tb); | 515 | struct icm *icm = tb_priv(tb); |
| 393 | bool authorized = false; | 516 | bool authorized = false; |
| 517 | struct tb_xdomain *xd; | ||
| 394 | u8 link, depth; | 518 | u8 link, depth; |
| 519 | bool boot; | ||
| 395 | u64 route; | 520 | u64 route; |
| 396 | int ret; | 521 | int ret; |
| 397 | 522 | ||
| @@ -399,6 +524,15 @@ icm_fr_device_connected(struct tb *tb, const struct icm_pkg_header *hdr) · 1 unmodified line | |||
| 399 | depth = (pkg->link_info & ICM_LINK_INFO_DEPTH_MASK) >> | 524 | depth = (pkg->link_info & ICM_LINK_INFO_DEPTH_MASK) >> |
| 400 | ICM_LINK_INFO_DEPTH_SHIFT; | 525 | ICM_LINK_INFO_DEPTH_SHIFT; |
| 401 | authorized = pkg->link_info & ICM_LINK_INFO_APPROVED; | 526 | authorized = pkg->link_info & ICM_LINK_INFO_APPROVED; |
| 527 | security_level = (pkg->hdr.flags & ICM_FLAGS_SLEVEL_MASK) >> | ||
| 528 | ICM_FLAGS_SLEVEL_SHIFT; | ||
| 529 | boot = pkg->link_info & ICM_LINK_INFO_BOOT; | ||
| 530 | |||
| 531 | if (pkg->link_info & ICM_LINK_INFO_REJECTED) { | ||
| 532 | tb_info(tb, "switch at %u.%u was rejected by ICM firmware because topology limit exceeded\n", | ||
| 533 | link, depth); | ||
| 534 | return; | ||
| 535 | } | ||
| 402 | 536 | ||
| 403 | ret = icm->get_route(tb, link, depth, &route); | 537 | ret = icm->get_route(tb, link, depth, &route); |
| 404 | if (ret) { | 538 | if (ret) { |
| @@ -425,16 +559,8 @@ icm_fr_device_connected(struct tb *tb, const struct icm_pkg_header *hdr) · 20 unmodified lines | |||
| 425 | */ | 559 | */ |
| 426 | if (sw->depth == depth && sw_phy_port == phy_port && | 560 | if (sw->depth == depth && sw_phy_port == phy_port && |
| 427 | !!sw->authorized == authorized) { | 561 | !!sw->authorized == authorized) { |
| 428 | tb_port_at(tb_route(sw), parent_sw)->remote = NULL; | 562 | update_switch(parent_sw, sw, route, pkg->connection_id, |
| 429 | tb_port_at(route, parent_sw)->remote = | 563 | pkg->connection_key, link, depth, boot); |
| 430 | tb_upstream_port(sw); | ||
| 431 | sw->config.route_hi = upper_32_bits(route); | ||
| 432 | sw->config.route_lo = lower_32_bits(route); | ||
| 433 | sw->connection_id = pkg->connection_id; | ||
| 434 | sw->connection_key = pkg->connection_key; | ||
| 435 | sw->link = link; | ||
| 436 | sw->depth = depth; | ||
| 437 | sw->is_unplugged = false; | ||
| 438 | tb_switch_put(sw); | 564 | tb_switch_put(sw); |
| 439 | return; | 565 | return; |
| 440 | } | 566 | } |
| @@ -467,6 +593,13 @@ icm_fr_device_connected(struct tb *tb, const struct icm_pkg_header *hdr) · 26 unmodified lines | |||
| 467 | tb_switch_put(sw); | 593 | tb_switch_put(sw); |
| 468 | } | 594 | } |
| 469 | 595 | ||
| 596 | /* Remove existing XDomain connection if found */ | ||
| 597 | xd = tb_xdomain_find_by_link_depth(tb, link, depth); | ||
| 598 | if (xd) { | ||
| 599 | remove_xdomain(xd); | ||
| 600 | tb_xdomain_put(xd); | ||
| 601 | } | ||
| 602 | |||
| 470 | parent_sw = tb_switch_find_by_link_depth(tb, link, depth - 1); | 603 | parent_sw = tb_switch_find_by_link_depth(tb, link, depth - 1); |
| 471 | if (!parent_sw) { | 604 | if (!parent_sw) { |
| 472 | tb_err(tb, "failed to find parent switch for %u.%u\n", | 605 | tb_err(tb, "failed to find parent switch for %u.%u\n", |
| @@ -474,30 +607,10 @@ icm_fr_device_connected(struct tb *tb, const struct icm_pkg_header *hdr) · 1 unmodified line | |||
| 474 | return; | 607 | return; |
| 475 | } | 608 | } |
| 476 | 609 | ||
| 477 | sw = tb_switch_alloc(tb, &parent_sw->dev, route); | 610 | add_switch(parent_sw, route, &pkg->ep_uuid, pkg->connection_id, |
| 478 | if (!sw) { | 611 | pkg->connection_key, link, depth, security_level, |
| 479 | tb_switch_put(parent_sw); | 612 | authorized, boot); |
| 480 | return; | ||
| 481 | } | ||
| 482 | |||
| 483 | sw->uuid = kmemdup(&pkg->ep_uuid, sizeof(pkg->ep_uuid), GFP_KERNEL); | ||
| 484 | sw->connection_id = pkg->connection_id; | ||
| 485 | sw->connection_key = pkg->connection_key; | ||
| 486 | sw->link = link; | ||
| 487 | sw->depth = depth; | ||
| 488 | sw->authorized = authorized; | ||
| 489 | sw->security_level = (pkg->hdr.flags & ICM_FLAGS_SLEVEL_MASK) >> | ||
| 490 | ICM_FLAGS_SLEVEL_SHIFT; | ||
| 491 | |||
| 492 | /* Link the two switches now */ | ||
| 493 | tb_port_at(route, parent_sw)->remote = tb_upstream_port(sw); | ||
| 494 | tb_upstream_port(sw)->remote = tb_port_at(route, parent_sw); | ||
| 495 | 613 | ||
| 496 | ret = tb_switch_add(sw); | ||
| 497 | if (ret) { | ||
| 498 | tb_port_at(tb_route(sw), parent_sw)->remote = NULL; | ||
| 499 | tb_switch_put(sw); | ||
| 500 | } | ||
| 501 | tb_switch_put(parent_sw); | 614 | tb_switch_put(parent_sw); |
| 502 | } | 615 | } |
| 503 | 616 | ||
| @@ -529,15 +642,6 @@ icm_fr_device_disconnected(struct tb *tb, const struct icm_pkg_header *hdr) · 25 unmodified lines | |||
| 529 | tb_switch_put(sw); | 642 | tb_switch_put(sw); |
| 530 | } | 643 | } |
| 531 | 644 | ||
| 532 | static void remove_xdomain(struct tb_xdomain *xd) | ||
| 533 | { | ||
| 534 | struct tb_switch *sw; | ||
| 535 | |||
| 536 | sw = tb_to_switch(xd->dev.parent); | ||
| 537 | tb_port_at(xd->route, sw)->xdomain = NULL; | ||
| 538 | tb_xdomain_remove(xd); | ||
| 539 | } | ||
| 540 | |||
| 541 | static void | 645 | static void |
| 542 | icm_fr_xdomain_connected(struct tb *tb, const struct icm_pkg_header *hdr) | 646 | icm_fr_xdomain_connected(struct tb *tb, const struct icm_pkg_header *hdr) |
| 543 | { | 647 | { |
| @@ -577,9 +681,7 @@ icm_fr_xdomain_connected(struct tb *tb, const struct icm_pkg_header *hdr) · 33 unmodified lines | |||
| 577 | phy_port = phy_port_from_route(route, depth); | 681 | phy_port = phy_port_from_route(route, depth); |
| 578 | 682 | ||
| 579 | if (xd->depth == depth && xd_phy_port == phy_port) { | 683 | if (xd->depth == depth && xd_phy_port == phy_port) { |
| 580 | xd->link = link; | 684 | update_xdomain(xd, route, link); |
| 581 | xd->route = route; | ||
| 582 | xd->is_unplugged = false; | ||
| 583 | tb_xdomain_put(xd); | 685 | tb_xdomain_put(xd); |
| 584 | return; | 686 | return; |
| 585 | } | 687 | } |
| @@ -629,19 +731,8 @@ icm_fr_xdomain_connected(struct tb *tb, const struct icm_pkg_header *hdr) · 43 unmodified lines | |||
| 629 | return; | 731 | return; |
| 630 | } | 732 | } |
| 631 | 733 | ||
| 632 | xd = tb_xdomain_alloc(sw->tb, &sw->dev, route, | 734 | add_xdomain(sw, route, &pkg->local_uuid, &pkg->remote_uuid, link, |
| 633 | &pkg->local_uuid, &pkg->remote_uuid); | 735 | depth); |
| 634 | if (!xd) { | ||
| 635 | tb_switch_put(sw); | ||
| 636 | return; | ||
| 637 | } | ||
| 638 | |||
| 639 | xd->link = link; | ||
| 640 | xd->depth = depth; | ||
| 641 | |||
| 642 | tb_port_at(route, sw)->xdomain = xd; | ||
| 643 | |||
| 644 | tb_xdomain_add(xd); | ||
| 645 | tb_switch_put(sw); | 736 | tb_switch_put(sw); |
| 646 | } | 737 | } |
| 647 | 738 | ||
| @@ -664,6 +755,351 @@ icm_fr_xdomain_disconnected(struct tb *tb, const struct icm_pkg_header *hdr) · 16 unmodified lines | |||
| 664 | } | 755 | } |
| 665 | } | 756 | } |
| 666 | 757 | ||
| 758 | static int | ||
| 759 | icm_tr_driver_ready(struct tb *tb, enum tb_security_level *security_level, | ||
| 760 | size_t *nboot_acl) | ||
| 761 | { | ||
| 762 | struct icm_tr_pkg_driver_ready_response reply; | ||
| 763 | struct icm_pkg_driver_ready request = { | ||
| 764 | .hdr.code = ICM_DRIVER_READY, | ||
| 765 | }; | ||
| 766 | int ret; | ||
| 767 | |||
| 768 | memset(&reply, 0, sizeof(reply)); | ||
| 769 | ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply), | ||
| 770 | 1, 20000); | ||
| 771 | if (ret) | ||
| 772 | return ret; | ||
| 773 | |||
| 774 | if (security_level) | ||
| 775 | *security_level = reply.info & ICM_TR_INFO_SLEVEL_MASK; | ||
| 776 | if (nboot_acl) | ||
| 777 | *nboot_acl = (reply.info & ICM_TR_INFO_BOOT_ACL_MASK) >> | ||
| 778 | ICM_TR_INFO_BOOT_ACL_SHIFT; | ||
| 779 | return 0; | ||
| 780 | } | ||
| 781 | |||
| 782 | static int icm_tr_approve_switch(struct tb *tb, struct tb_switch *sw) | ||
| 783 | { | ||
| 784 | struct icm_tr_pkg_approve_device request; | ||
| 785 | struct icm_tr_pkg_approve_device reply; | ||
| 786 | int ret; | ||
| 787 | |||
| 788 | memset(&request, 0, sizeof(request)); | ||
| 789 | memcpy(&request.ep_uuid, sw->uuid, sizeof(request.ep_uuid)); | ||
| 790 | request.hdr.code = ICM_APPROVE_DEVICE; | ||
| 791 | request.route_lo = sw->config.route_lo; | ||
| 792 | request.route_hi = sw->config.route_hi; | ||
| 793 | request.connection_id = sw->connection_id; | ||
| 794 | |||
| 795 | memset(&reply, 0, sizeof(reply)); | ||
| 796 | ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply), | ||
| 797 | 1, ICM_APPROVE_TIMEOUT); | ||
| 798 | if (ret) | ||
| 799 | return ret; | ||
| 800 | |||
| 801 | if (reply.hdr.flags & ICM_FLAGS_ERROR) { | ||
| 802 | tb_warn(tb, "PCIe tunnel creation failed\n"); | ||
| 803 | return -EIO; | ||
| 804 | } | ||
| 805 | |||
| 806 | return 0; | ||
| 807 | } | ||
| 808 | |||
| 809 | static int icm_tr_add_switch_key(struct tb *tb, struct tb_switch *sw) | ||
| 810 | { | ||
| 811 | struct icm_tr_pkg_add_device_key_response reply; | ||
| 812 | struct icm_tr_pkg_add_device_key request; | ||
| 813 | int ret; | ||
| 814 | |||
| 815 | memset(&request, 0, sizeof(request)); | ||
| 816 | memcpy(&request.ep_uuid, sw->uuid, sizeof(request.ep_uuid)); | ||
| 817 | request.hdr.code = ICM_ADD_DEVICE_KEY; | ||
| 818 | request.route_lo = sw->config.route_lo; | ||
| 819 | request.route_hi = sw->config.route_hi; | ||
| 820 | request.connection_id = sw->connection_id; | ||
| 821 | memcpy(request.key, sw->key, TB_SWITCH_KEY_SIZE); | ||
| 822 | |||
| 823 | memset(&reply, 0, sizeof(reply)); | ||
| 824 | ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply), | ||
| 825 | 1, ICM_TIMEOUT); | ||
| 826 | if (ret) | ||
| 827 | return ret; | ||
| 828 | |||
| 829 | if (reply.hdr.flags & ICM_FLAGS_ERROR) { | ||
| 830 | tb_warn(tb, "Adding key to switch failed\n"); | ||
| 831 | return -EIO; | ||
| 832 | } | ||
| 833 | |||
| 834 | return 0; | ||
| 835 | } | ||
| 836 | |||
| 837 | static int icm_tr_challenge_switch_key(struct tb *tb, struct tb_switch *sw, | ||
| 838 | const u8 *challenge, u8 *response) | ||
| 839 | { | ||
| 840 | struct icm_tr_pkg_challenge_device_response reply; | ||
| 841 | struct icm_tr_pkg_challenge_device request; | ||
| 842 | int ret; | ||
| 843 | |||
| 844 | memset(&request, 0, sizeof(request)); | ||
| 845 | memcpy(&request.ep_uuid, sw->uuid, sizeof(request.ep_uuid)); | ||
| 846 | request.hdr.code = ICM_CHALLENGE_DEVICE; | ||
| 847 | request.route_lo = sw->config.route_lo; | ||
| 848 | request.route_hi = sw->config.route_hi; | ||
| 849 | request.connection_id = sw->connection_id; | ||
| 850 | memcpy(request.challenge, challenge, TB_SWITCH_KEY_SIZE); | ||
| 851 | |||
| 852 | memset(&reply, 0, sizeof(reply)); | ||
| 853 | ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply), | ||
| 854 | 1, ICM_TIMEOUT); | ||
| 855 | if (ret) | ||
| 856 | return ret; | ||
| 857 | |||
| 858 | if (reply.hdr.flags & ICM_FLAGS_ERROR) | ||
| 859 | return -EKEYREJECTED; | ||
| 860 | if (reply.hdr.flags & ICM_FLAGS_NO_KEY) | ||
| 861 | return -ENOKEY; | ||
| 862 | |||
| 863 | memcpy(response, reply.response, TB_SWITCH_KEY_SIZE); | ||
| 864 | |||
| 865 | return 0; | ||
| 866 | } | ||
| 867 | |||
| 868 | static int icm_tr_approve_xdomain_paths(struct tb *tb, struct tb_xdomain *xd) | ||
| 869 | { | ||
| 870 | struct icm_tr_pkg_approve_xdomain_response reply; | ||
| 871 | struct icm_tr_pkg_approve_xdomain request; | ||
| 872 | int ret; | ||
| 873 | |||
| 874 | memset(&request, 0, sizeof(request)); | ||
| 875 | request.hdr.code = ICM_APPROVE_XDOMAIN; | ||
| 876 | request.route_hi = upper_32_bits(xd->route); | ||
| 877 | request.route_lo = lower_32_bits(xd->route); | ||
| 878 | request.transmit_path = xd->transmit_path; | ||
| 879 | request.transmit_ring = xd->transmit_ring; | ||
| 880 | request.receive_path = xd->receive_path; | ||
| 881 | request.receive_ring = xd->receive_ring; | ||
| 882 | memcpy(&request.remote_uuid, xd->remote_uuid, sizeof(*xd->remote_uuid)); | ||
| 883 | |||
| 884 | memset(&reply, 0, sizeof(reply)); | ||
| 885 | ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply), | ||
| 886 | 1, ICM_TIMEOUT); | ||
| 887 | if (ret) | ||
| 888 | return ret; | ||
| 889 | |||
| 890 | if (reply.hdr.flags & ICM_FLAGS_ERROR) | ||
| 891 | return -EIO; | ||
| 892 | |||
| 893 | return 0; | ||
| 894 | } | ||
| 895 | |||
| 896 | static int icm_tr_xdomain_tear_down(struct tb *tb, struct tb_xdomain *xd, | ||
| 897 | int stage) | ||
| 898 | { | ||
| 899 | struct icm_tr_pkg_disconnect_xdomain_response reply; | ||
| 900 | struct icm_tr_pkg_disconnect_xdomain request; | ||
| 901 | int ret; | ||
| 902 | |||
| 903 | memset(&request, 0, sizeof(request)); | ||
| 904 | request.hdr.code = ICM_DISCONNECT_XDOMAIN; | ||
| 905 | request.stage = stage; | ||
| 906 | request.route_hi = upper_32_bits(xd->route); | ||
| 907 | request.route_lo = lower_32_bits(xd->route); | ||
| 908 | memcpy(&request.remote_uuid, xd->remote_uuid, sizeof(*xd->remote_uuid)); | ||
| 909 | |||
| 910 | memset(&reply, 0, sizeof(reply)); | ||
| 911 | ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply), | ||
| 912 | 1, ICM_TIMEOUT); | ||
| 913 | if (ret) | ||
| 914 | return ret; | ||
| 915 | |||
| 916 | if (reply.hdr.flags & ICM_FLAGS_ERROR) | ||
| 917 | return -EIO; | ||
| 918 | |||
| 919 | return 0; | ||
| 920 | } | ||
| 921 | |||
| 922 | static int icm_tr_disconnect_xdomain_paths(struct tb *tb, struct tb_xdomain *xd) | ||
| 923 | { | ||
| 924 | int ret; | ||
| 925 | |||
| 926 | ret = icm_tr_xdomain_tear_down(tb, xd, 1); | ||
| 927 | if (ret) | ||
| 928 | return ret; | ||
| 929 | |||
| 930 | usleep_range(10, 50); | ||
| 931 | return icm_tr_xdomain_tear_down(tb, xd, 2); | ||
| 932 | } | ||
| 933 | |||
| 934 | static void | ||
| 935 | icm_tr_device_connected(struct tb *tb, const struct icm_pkg_header *hdr) | ||
| 936 | { | ||
| 937 | const struct icm_tr_event_device_connected *pkg = | ||
| 938 | (const struct icm_tr_event_device_connected *)hdr; | ||
| 939 | enum tb_security_level security_level; | ||
| 940 | struct tb_switch *sw, *parent_sw; | ||
| 941 | struct tb_xdomain *xd; | ||
| 942 | bool authorized, boot; | ||
| 943 | u64 route; | ||
| 944 | |||
| 945 | /* | ||
| 946 | * Currently we don't use the QoS information coming with the | ||
| 947 | * device connected message so simply just ignore that extra | ||
| 948 | * packet for now. | ||
| 949 | */ | ||
| 950 | if (pkg->hdr.packet_id) | ||
| 951 | return; | ||
| 952 | |||
| 953 | /* | ||
| 954 | * After NVM upgrade adding root switch device fails because we | ||
| 955 | * initiated reset. During that time ICM might still send device | ||
| 956 | * connected message which we ignore here. | ||
| 957 | */ | ||
| 958 | if (!tb->root_switch) | ||
| 959 | return; | ||
| 960 | |||
| 961 | route = get_route(pkg->route_hi, pkg->route_lo); | ||
| 962 | authorized = pkg->link_info & ICM_LINK_INFO_APPROVED; | ||
| 963 | security_level = (pkg->hdr.flags & ICM_FLAGS_SLEVEL_MASK) >> | ||
| 964 | ICM_FLAGS_SLEVEL_SHIFT; | ||
| 965 | boot = pkg->link_info & ICM_LINK_INFO_BOOT; | ||
| 966 | |||
| 967 | if (pkg->link_info & ICM_LINK_INFO_REJECTED) { | ||
| 968 | tb_info(tb, "switch at %llx was rejected by ICM firmware because topology limit exceeded\n", | ||
| 969 | route); | ||
| 970 | return; | ||
| 971 | } | ||
| 972 | |||
| 973 | sw = tb_switch_find_by_uuid(tb, &pkg->ep_uuid); | ||
| 974 | if (sw) { | ||
| 975 | /* Update the switch if it is still in the same place */ | ||
| 976 | if (tb_route(sw) == route && !!sw->authorized == authorized) { | ||
| 977 | parent_sw = tb_to_switch(sw->dev.parent); | ||
| 978 | update_switch(parent_sw, sw, route, pkg->connection_id, | ||
| 979 | 0, 0, 0, boot); | ||
| 980 | tb_switch_put(sw); | ||
| 981 | return; | ||
| 982 | } | ||
| 983 | |||
| 984 | remove_switch(sw); | ||
| 985 | tb_switch_put(sw); | ||
| 986 | } | ||
| 987 | |||
| 988 | /* Another switch with the same address */ | ||
| 989 | sw = tb_switch_find_by_route(tb, route); | ||
| 990 | if (sw) { | ||
| 991 | remove_switch(sw); | ||
| 992 | tb_switch_put(sw); | ||
| 993 | } | ||
| 994 | |||
| 995 | /* XDomain connection with the same address */ | ||
| 996 | xd = tb_xdomain_find_by_route(tb, route); | ||
| 997 | if (xd) { | ||
| 998 | remove_xdomain(xd); | ||
| 999 | tb_xdomain_put(xd); | ||
| 1000 | } | ||
| 1001 | |||
| 1002 | parent_sw = tb_switch_find_by_route(tb, get_parent_route(route)); | ||
| 1003 | if (!parent_sw) { | ||
| 1004 | tb_err(tb, "failed to find parent switch for %llx\n", route); | ||
| 1005 | return; | ||
| 1006 | } | ||
| 1007 | |||
| 1008 | add_switch(parent_sw, route, &pkg->ep_uuid, pkg->connection_id, | ||
| 1009 | 0, 0, 0, security_level, authorized, boot); | ||
| 1010 | |||
| 1011 | tb_switch_put(parent_sw); | ||
| 1012 | } | ||
| 1013 | |||
| 1014 | static void | ||
| 1015 | icm_tr_device_disconnected(struct tb *tb, const struct icm_pkg_header *hdr) | ||
| 1016 | { | ||
| 1017 | const struct icm_tr_event_device_disconnected *pkg = | ||
| 1018 | (const struct icm_tr_event_device_disconnected *)hdr; | ||
| 1019 | struct tb_switch *sw; | ||
| 1020 | u64 route; | ||
| 1021 | |||
| 1022 | route = get_route(pkg->route_hi, pkg->route_lo); | ||
| 1023 | |||
| 1024 | sw = tb_switch_find_by_route(tb, route); | ||
| 1025 | if (!sw) { | ||
| 1026 | tb_warn(tb, "no switch exists at %llx, ignoring\n", route); | ||
| 1027 | return; | ||
| 1028 | } | ||
| 1029 | |||
| 1030 | remove_switch(sw); | ||
| 1031 | tb_switch_put(sw); | ||
| 1032 | } | ||
| 1033 | |||
| 1034 | static void | ||
| 1035 | icm_tr_xdomain_connected(struct tb *tb, const struct icm_pkg_header *hdr) | ||
| 1036 | { | ||
| 1037 | const struct icm_tr_event_xdomain_connected *pkg = | ||
| 1038 | (const struct icm_tr_event_xdomain_connected *)hdr; | ||
| 1039 | struct tb_xdomain *xd; | ||
| 1040 | struct tb_switch *sw; | ||
| 1041 | u64 route; | ||
| 1042 | |||
| 1043 | if (!tb->root_switch) | ||
| 1044 | return; | ||
| 1045 | |||
| 1046 | route = get_route(pkg->local_route_hi, pkg->local_route_lo); | ||
| 1047 | |||
| 1048 | xd = tb_xdomain_find_by_uuid(tb, &pkg->remote_uuid); | ||
| 1049 | if (xd) { | ||
| 1050 | if (xd->route == route) { | ||
| 1051 | update_xdomain(xd, route, 0); | ||
| 1052 | tb_xdomain_put(xd); | ||
| 1053 | return; | ||
| 1054 | } | ||
| 1055 | |||
| 1056 | remove_xdomain(xd); | ||
| 1057 | tb_xdomain_put(xd); | ||
| 1058 | } | ||
| 1059 | |||
| 1060 | /* An existing xdomain with the same address */ | ||
| 1061 | xd = tb_xdomain_find_by_route(tb, route); | ||
| 1062 | if (xd) { | ||
| 1063 | remove_xdomain(xd); | ||
| 1064 | tb_xdomain_put(xd); | ||
| 1065 | } | ||
| 1066 | |||
| 1067 | /* | ||
| 1068 | * If the user disconnected a switch during suspend and | ||
| 1069 | * connected another host to the same port, remove the switch | ||
| 1070 | * first. | ||
| 1071 | */ | ||
| 1072 | sw = get_switch_at_route(tb->root_switch, route); | ||
| 1073 | if (sw) | ||
| 1074 | remove_switch(sw); | ||
| 1075 | |||
| 1076 | sw = tb_switch_find_by_route(tb, get_parent_route(route)); | ||
| 1077 | if (!sw) { | ||
| 1078 | tb_warn(tb, "no switch exists at %llx, ignoring\n", route); | ||
| 1079 | return; | ||
| 1080 | } | ||
| 1081 | |||
| 1082 | add_xdomain(sw, route, &pkg->local_uuid, &pkg->remote_uuid, 0, 0); | ||
| 1083 | tb_switch_put(sw); | ||
| 1084 | } | ||
| 1085 | |||
| 1086 | static void | ||
| 1087 | icm_tr_xdomain_disconnected(struct tb *tb, const struct icm_pkg_header *hdr) | ||
| 1088 | { | ||
| 1089 | const struct icm_tr_event_xdomain_disconnected *pkg = | ||
| 1090 | (const struct icm_tr_event_xdomain_disconnected *)hdr; | ||
| 1091 | struct tb_xdomain *xd; | ||
| 1092 | u64 route; | ||
| 1093 | |||
| 1094 | route = get_route(pkg->route_hi, pkg->route_lo); | ||
| 1095 | |||
| 1096 | xd = tb_xdomain_find_by_route(tb, route); | ||
| 1097 | if (xd) { | ||
| 1098 | remove_xdomain(xd); | ||
| 1099 | tb_xdomain_put(xd); | ||
| 1100 | } | ||
| 1101 | } | ||
| 1102 | |||
| 667 | static struct pci_dev *get_upstream_port(struct pci_dev *pdev) | 1103 | static struct pci_dev *get_upstream_port(struct pci_dev *pdev) |
| 668 | { | 1104 | { |
| 669 | struct pci_dev *parent; | 1105 | struct pci_dev *parent; |
| @@ -728,14 +1164,14 @@ static bool icm_ar_is_supported(struct tb *tb) · 58 unmodified lines | |||
| 728 | static int icm_ar_get_mode(struct tb *tb) | 1164 | static int icm_ar_get_mode(struct tb *tb) |
| 729 | { | 1165 | { |
| 730 | struct tb_nhi *nhi = tb->nhi; | 1166 | struct tb_nhi *nhi = tb->nhi; |
| 731 | int retries = 5; | 1167 | int retries = 60; |
| 732 | u32 val; | 1168 | u32 val; |
| 733 | 1169 | ||
| 734 | do { | 1170 | do { |
| 735 | val = ioread32(nhi->iobase + REG_FW_STS); | 1171 | val = ioread32(nhi->iobase + REG_FW_STS); |
| 736 | if (val & REG_FW_STS_NVM_AUTH_DONE) | 1172 | if (val & REG_FW_STS_NVM_AUTH_DONE) |
| 737 | break; | 1173 | break; |
| 738 | msleep(30); | 1174 | msleep(50); |
| 739 | } while (--retries); | 1175 | } while (--retries); |
| 740 | 1176 | ||
| 741 | if (!retries) { | 1177 | if (!retries) { |
| @@ -746,6 +1182,30 @@ static int icm_ar_get_mode(struct tb *tb) · 4 unmodified lines | |||
| 746 | return nhi_mailbox_mode(nhi); | 1182 | return nhi_mailbox_mode(nhi); |
| 747 | } | 1183 | } |
| 748 | 1184 | ||
| 1185 | static int | ||
| 1186 | icm_ar_driver_ready(struct tb *tb, enum tb_security_level *security_level, | ||
| 1187 | size_t *nboot_acl) | ||
| 1188 | { | ||
| 1189 | struct icm_ar_pkg_driver_ready_response reply; | ||
| 1190 | struct icm_pkg_driver_ready request = { | ||
| 1191 | .hdr.code = ICM_DRIVER_READY, | ||
| 1192 | }; | ||
| 1193 | int ret; | ||
| 1194 | |||
| 1195 | memset(&reply, 0, sizeof(reply)); | ||
| 1196 | ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply), | ||
| 1197 | 1, ICM_TIMEOUT); | ||
| 1198 | if (ret) | ||
| 1199 | return ret; | ||
| 1200 | |||
| 1201 | if (security_level) | ||
| 1202 | *security_level = reply.info & ICM_AR_INFO_SLEVEL_MASK; | ||
| 1203 | if (nboot_acl && (reply.info & ICM_AR_INFO_BOOT_ACL_SUPPORTED)) | ||
| 1204 | *nboot_acl = (reply.info & ICM_AR_INFO_BOOT_ACL_MASK) >> | ||
| 1205 | ICM_AR_INFO_BOOT_ACL_SHIFT; | ||
| 1206 | return 0; | ||
| 1207 | } | ||
| 1208 | |||
| 749 | static int icm_ar_get_route(struct tb *tb, u8 link, u8 depth, u64 *route) | 1209 | static int icm_ar_get_route(struct tb *tb, u8 link, u8 depth, u64 *route) |
| 750 | { | 1210 | { |
| 751 | struct icm_ar_pkg_get_route_response reply; | 1211 | struct icm_ar_pkg_get_route_response reply; |
| @@ -768,6 +1228,87 @@ static int icm_ar_get_route(struct tb *tb, u8 link, u8 depth, u64 *route) · 16 unmodified lines | |||
| 768 | return 0; | 1228 | return 0; |
| 769 | } | 1229 | } |
| 770 | 1230 | ||
| 1231 | static int icm_ar_get_boot_acl(struct tb *tb, uuid_t *uuids, size_t nuuids) | ||
| 1232 | { | ||
| 1233 | struct icm_ar_pkg_preboot_acl_response reply; | ||
| 1234 | struct icm_ar_pkg_preboot_acl request = { | ||
| 1235 | .hdr = { .code = ICM_PREBOOT_ACL }, | ||
| 1236 | }; | ||
| 1237 | int ret, i; | ||
| 1238 | |||
| 1239 | memset(&reply, 0, sizeof(reply)); | ||
| 1240 | ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply), | ||
| 1241 | 1, ICM_TIMEOUT); | ||
| 1242 | if (ret) | ||
| 1243 | return ret; | ||
| 1244 | |||
| 1245 | if (reply.hdr.flags & ICM_FLAGS_ERROR) | ||
| 1246 | return -EIO; | ||
| 1247 | |||
| 1248 | for (i = 0; i < nuuids; i++) { | ||
| 1249 | u32 *uuid = (u32 *)&uuids[i]; | ||
| 1250 | |||
| 1251 | uuid[0] = reply.acl[i].uuid_lo; | ||
| 1252 | uuid[1] = reply.acl[i].uuid_hi; | ||
| 1253 | |||
| 1254 | if (uuid[0] == 0xffffffff && uuid[1] == 0xffffffff) { | ||
| 1255 | /* Map empty entries to null UUID */ | ||
| 1256 | uuid[0] = 0; | ||
| 1257 | uuid[1] = 0; | ||
| 1258 | } else { | ||
| 1259 | /* Upper two DWs are always one's */ | ||
| 1260 | uuid[2] = 0xffffffff; | ||
| 1261 | uuid[3] = 0xffffffff; | ||
| 1262 | } | ||
| 1263 | } | ||
| 1264 | |||
| 1265 | return ret; | ||
| 1266 | } | ||
| 1267 | |||
| 1268 | static int icm_ar_set_boot_acl(struct tb *tb, const uuid_t *uuids, | ||
| 1269 | size_t nuuids) | ||
| 1270 | { | ||
| 1271 | struct icm_ar_pkg_preboot_acl_response reply; | ||
| 1272 | struct icm_ar_pkg_preboot_acl request = { | ||
| 1273 | .hdr = { | ||
| 1274 | .code = ICM_PREBOOT_ACL, | ||
| 1275 | .flags = ICM_FLAGS_WRITE, | ||
| 1276 | }, | ||
| 1277 | }; | ||
| 1278 | int ret, i; | ||
| 1279 | |||
| 1280 | for (i = 0; i < nuuids; i++) { | ||
| 1281 | const u32 *uuid = (const u32 *)&uuids[i]; | ||
| 1282 | |||
| 1283 | if (uuid_is_null(&uuids[i])) { | ||
| 1284 | /* | ||
| 1285 | * Map null UUID to the empty (all one) entries | ||
| 1286 | * for ICM. | ||
| 1287 | */ | ||
| 1288 | request.acl[i].uuid_lo = 0xffffffff; | ||
| 1289 | request.acl[i].uuid_hi = 0xffffffff; | ||
| 1290 | } else { | ||
| 1291 | /* Two high DWs need to be set to all one */ | ||
| 1292 | if (uuid[2] != 0xffffffff || uuid[3] != 0xffffffff) | ||
| 1293 | return -EINVAL; | ||
| 1294 | |||
| 1295 | request.acl[i].uuid_lo = uuid[0]; | ||
| 1296 | request.acl[i].uuid_hi = uuid[1]; | ||
| 1297 | } | ||
| 1298 | } | ||
| 1299 | |||
| 1300 | memset(&reply, 0, sizeof(reply)); | ||
| 1301 | ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply), | ||
| 1302 | 1, ICM_TIMEOUT); | ||
| 1303 | if (ret) | ||
| 1304 | return ret; | ||
| 1305 | |||
| 1306 | if (reply.hdr.flags & ICM_FLAGS_ERROR) | ||
| 1307 | return -EIO; | ||
| 1308 | |||
| 1309 | return 0; | ||
| 1310 | } | ||
| 1311 | |||
| 771 | static void icm_handle_notification(struct work_struct *work) | 1312 | static void icm_handle_notification(struct work_struct *work) |
| 772 | { | 1313 | { |
| 773 | struct icm_notification *n = container_of(work, typeof(*n), work); | 1314 | struct icm_notification *n = container_of(work, typeof(*n), work); |
| @@ -814,23 +1355,18 @@ static void icm_handle_event(struct tb *tb, enum tb_cfg_pkg_type type, · 40 unmodified lines | |||
| 814 | } | 1355 | } |
| 815 | 1356 | ||
| 816 | static int | 1357 | static int |
| 817 | __icm_driver_ready(struct tb *tb, enum tb_security_level *security_level) | 1358 | __icm_driver_ready(struct tb *tb, enum tb_security_level *security_level, |
| 1359 | size_t *nboot_acl) | ||
| 818 | { | 1360 | { |
| 819 | struct icm_pkg_driver_ready_response reply; | 1361 | struct icm *icm = tb_priv(tb); |
| 820 | struct icm_pkg_driver_ready request = { | 1362 | unsigned int retries = 50; |
| 821 | .hdr.code = ICM_DRIVER_READY, | ||
| 822 | }; | ||
| 823 | unsigned int retries = 10; | ||
| 824 | int ret; | 1363 | int ret; |
| 825 | 1364 | ||
| 826 | memset(&reply, 0, sizeof(reply)); | 1365 | ret = icm->driver_ready(tb, security_level, nboot_acl); |
| 827 | ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply), | 1366 | if (ret) { |
| 828 | 1, ICM_TIMEOUT); | 1367 | tb_err(tb, "failed to send driver ready to ICM\n"); |
| 829 | if (ret) | ||
| 830 | return ret; | 1368 | return ret; |
| 831 | 1369 | } | |
| 832 | if (security_level) | ||
| 833 | *security_level = reply.security_level & 0xf; | ||
| 834 | 1370 | ||
| 835 | /* | 1371 | /* |
| 836 | * Hold on here until the switch config space is accessible so | 1372 | * Hold on here until the switch config space is accessible so |
| @@ -848,6 +1384,7 @@ __icm_driver_ready(struct tb *tb, enum tb_security_level *security_level) · 11 unmodified lines | |||
| 848 | msleep(50); | 1384 | msleep(50); |
| 849 | } while (--retries); | 1385 | } while (--retries); |
| 850 | 1386 | ||
| 1387 | tb_err(tb, "failed to read root switch config space, giving up\n"); | ||
| 851 | return -ETIMEDOUT; | 1388 | return -ETIMEDOUT; |
| 852 | } | 1389 | } |
| 853 | 1390 | ||
| @@ -915,6 +1452,9 @@ static int icm_firmware_reset(struct tb *tb, struct tb_nhi *nhi) · 61 unmodified lines | |||
| 915 | struct icm *icm = tb_priv(tb); | 1452 | struct icm *icm = tb_priv(tb); |
| 916 | u32 val; | 1453 | u32 val; |
| 917 | 1454 | ||
| 1455 | if (!icm->upstream_port) | ||
| 1456 | return -ENODEV; | ||
| 1457 | |||
| 918 | /* Put ARC to wait for CIO reset event to happen */ | 1458 | /* Put ARC to wait for CIO reset event to happen */ |
| 919 | val = ioread32(nhi->iobase + REG_FW_STS); | 1459 | val = ioread32(nhi->iobase + REG_FW_STS); |
| 920 | val |= REG_FW_STS_CIO_RESET_REQ; | 1460 | val |= REG_FW_STS_CIO_RESET_REQ; |
| @@ -1054,6 +1594,9 @@ static int icm_firmware_init(struct tb *tb) · 133 unmodified lines | |||
| 1054 | break; | 1594 | break; |
| 1055 | 1595 | ||
| 1056 | default: | 1596 | default: |
| 1597 | if (ret < 0) | ||
| 1598 | return ret; | ||
| 1599 | |||
| 1057 | tb_err(tb, "ICM firmware is in wrong mode: %u\n", ret); | 1600 | tb_err(tb, "ICM firmware is in wrong mode: %u\n", ret); |
| 1058 | return -ENODEV; | 1601 | return -ENODEV; |
| 1059 | } | 1602 | } |
| @@ -1089,7 +1632,18 @@ static int icm_driver_ready(struct tb *tb) · 29 unmodified lines | |||
| 1089 | return 0; | 1632 | return 0; |
| 1090 | } | 1633 | } |
| 1091 | 1634 | ||
| 1092 | return __icm_driver_ready(tb, &tb->security_level); | 1635 | ret = __icm_driver_ready(tb, &tb->security_level, &tb->nboot_acl); |
| 1636 | if (ret) | ||
| 1637 | return ret; | ||
| 1638 | |||
| 1639 | /* | ||
| 1640 | * Make sure the number of supported preboot ACL matches what we | ||
| 1641 | * expect or disable the whole feature. | ||
| 1642 | */ | ||
| 1643 | if (tb->nboot_acl > icm->max_boot_acl) | ||
| 1644 | tb->nboot_acl = 0; | ||
| 1645 | |||
| 1646 | return 0; | ||
| 1093 | } | 1647 | } |
| 1094 | 1648 | ||
| 1095 | static int icm_suspend(struct tb *tb) | 1649 | static int icm_suspend(struct tb *tb) |
| @@ -1185,7 +1739,7 @@ static void icm_complete(struct tb *tb) · 89 unmodified lines | |||
| 1185 | * Now all existing children should be resumed, start events | 1739 | * Now all existing children should be resumed, start events |
| 1186 | * from ICM to get updated status. | 1740 | * from ICM to get updated status. |
| 1187 | */ | 1741 | */ |
| 1188 | __icm_driver_ready(tb, NULL); | 1742 | __icm_driver_ready(tb, NULL, NULL); |
| 1189 | 1743 | ||
| 1190 | /* | 1744 | /* |
| 1191 | * We do not get notifications of devices that have been | 1745 | * We do not get notifications of devices that have been |
| @@ -1238,7 +1792,7 @@ static int icm_disconnect_pcie_paths(struct tb *tb) · 46 unmodified lines | |||
| 1238 | return nhi_mailbox_cmd(tb->nhi, NHI_MAILBOX_DISCONNECT_PCIE_PATHS, 0); | 1792 | return nhi_mailbox_cmd(tb->nhi, NHI_MAILBOX_DISCONNECT_PCIE_PATHS, 0); |
| 1239 | } | 1793 | } |
| 1240 | 1794 | ||
| 1241 | /* Falcon Ridge and Alpine Ridge */ | 1795 | /* Falcon Ridge */ |
| 1242 | static const struct tb_cm_ops icm_fr_ops = { | 1796 | static const struct tb_cm_ops icm_fr_ops = { |
| 1243 | .driver_ready = icm_driver_ready, | 1797 | .driver_ready = icm_driver_ready, |
| 1244 | .start = icm_start, | 1798 | .start = icm_start, |
| @@ -1254,6 +1808,42 @@ static const struct tb_cm_ops icm_fr_ops = { · 9 unmodified lines | |||
| 1254 | .disconnect_xdomain_paths = icm_fr_disconnect_xdomain_paths, | 1808 | .disconnect_xdomain_paths = icm_fr_disconnect_xdomain_paths, |
| 1255 | }; | 1809 | }; |
| 1256 | 1810 | ||
| 1811 | /* Alpine Ridge */ | ||
| 1812 | static const struct tb_cm_ops icm_ar_ops = { | ||
| 1813 | .driver_ready = icm_driver_ready, | ||
| 1814 | .start = icm_start, | ||
| 1815 | .stop = icm_stop, | ||
| 1816 | .suspend = icm_suspend, | ||
| 1817 | .complete = icm_complete, | ||
| 1818 | .handle_event = icm_handle_event, | ||
| 1819 | .get_boot_acl = icm_ar_get_boot_acl, | ||
| 1820 | .set_boot_acl = icm_ar_set_boot_acl, | ||
| 1821 | .approve_switch = icm_fr_approve_switch, | ||
| 1822 | .add_switch_key = icm_fr_add_switch_key, | ||
| 1823 | .challenge_switch_key = icm_fr_challenge_switch_key, | ||
| 1824 | .disconnect_pcie_paths = icm_disconnect_pcie_paths, | ||
| 1825 | .approve_xdomain_paths = icm_fr_approve_xdomain_paths, | ||
| 1826 | .disconnect_xdomain_paths = icm_fr_disconnect_xdomain_paths, | ||
| 1827 | }; | ||
| 1828 | |||
| 1829 | /* Titan Ridge */ | ||
| 1830 | static const struct tb_cm_ops icm_tr_ops = { | ||
| 1831 | .driver_ready = icm_driver_ready, | ||
| 1832 | .start = icm_start, | ||
| 1833 | .stop = icm_stop, | ||
| 1834 | .suspend = icm_suspend, | ||
| 1835 | .complete = icm_complete, | ||
| 1836 | .handle_event = icm_handle_event, | ||
| 1837 | .get_boot_acl = icm_ar_get_boot_acl, | ||
| 1838 | .set_boot_acl = icm_ar_set_boot_acl, | ||
| 1839 | .approve_switch = icm_tr_approve_switch, | ||
| 1840 | .add_switch_key = icm_tr_add_switch_key, | ||
| 1841 | .challenge_switch_key = icm_tr_challenge_switch_key, | ||
| 1842 | .disconnect_pcie_paths = icm_disconnect_pcie_paths, | ||
| 1843 | .approve_xdomain_paths = icm_tr_approve_xdomain_paths, | ||
| 1844 | .disconnect_xdomain_paths = icm_tr_disconnect_xdomain_paths, | ||
| 1845 | }; | ||
| 1846 | |||
| 1257 | struct tb *icm_probe(struct tb_nhi *nhi) | 1847 | struct tb *icm_probe(struct tb_nhi *nhi) |
| 1258 | { | 1848 | { |
| 1259 | struct icm *icm; | 1849 | struct icm *icm; |
| @@ -1272,6 +1862,7 @@ struct tb *icm_probe(struct tb_nhi *nhi) · 12 unmodified lines | |||
| 1272 | case PCI_DEVICE_ID_INTEL_FALCON_RIDGE_4C_NHI: | 1862 | case PCI_DEVICE_ID_INTEL_FALCON_RIDGE_4C_NHI: |
| 1273 | icm->is_supported = icm_fr_is_supported; | 1863 | icm->is_supported = icm_fr_is_supported; |
| 1274 | icm->get_route = icm_fr_get_route; | 1864 | icm->get_route = icm_fr_get_route; |
| 1865 | icm->driver_ready = icm_fr_driver_ready; | ||
| 1275 | icm->device_connected = icm_fr_device_connected; | 1866 | icm->device_connected = icm_fr_device_connected; |
| 1276 | icm->device_disconnected = icm_fr_device_disconnected; | 1867 | icm->device_disconnected = icm_fr_device_disconnected; |
| 1277 | icm->xdomain_connected = icm_fr_xdomain_connected; | 1868 | icm->xdomain_connected = icm_fr_xdomain_connected; |
| @@ -1284,14 +1875,29 @@ struct tb *icm_probe(struct tb_nhi *nhi) · 6 unmodified lines | |||
| 1284 | case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_LP_NHI: | 1875 | case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_LP_NHI: |
| 1285 | case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_4C_NHI: | 1876 | case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_4C_NHI: |
| 1286 | case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_2C_NHI: | 1877 | case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_2C_NHI: |
| 1878 | icm->max_boot_acl = ICM_AR_PREBOOT_ACL_ENTRIES; | ||
| 1287 | icm->is_supported = icm_ar_is_supported; | 1879 | icm->is_supported = icm_ar_is_supported; |
| 1288 | icm->get_mode = icm_ar_get_mode; | 1880 | icm->get_mode = icm_ar_get_mode; |
| 1289 | icm->get_route = icm_ar_get_route; | 1881 | icm->get_route = icm_ar_get_route; |
| 1882 | icm->driver_ready = icm_ar_driver_ready; | ||
| 1290 | icm->device_connected = icm_fr_device_connected; | 1883 | icm->device_connected = icm_fr_device_connected; |
| 1291 | icm->device_disconnected = icm_fr_device_disconnected; | 1884 | icm->device_disconnected = icm_fr_device_disconnected; |
| 1292 | icm->xdomain_connected = icm_fr_xdomain_connected; | 1885 | icm->xdomain_connected = icm_fr_xdomain_connected; |
| 1293 | icm->xdomain_disconnected = icm_fr_xdomain_disconnected; | 1886 | icm->xdomain_disconnected = icm_fr_xdomain_disconnected; |
| 1294 | tb->cm_ops = &icm_fr_ops; | 1887 | tb->cm_ops = &icm_ar_ops; |
| 1888 | break; | ||
| 1889 | |||
| 1890 | case PCI_DEVICE_ID_INTEL_TITAN_RIDGE_2C_NHI: | ||
| 1891 | case PCI_DEVICE_ID_INTEL_TITAN_RIDGE_4C_NHI: | ||
| 1892 | icm->max_boot_acl = ICM_AR_PREBOOT_ACL_ENTRIES; | ||
| 1893 | icm->is_supported = icm_ar_is_supported; | ||
| 1894 | icm->get_mode = icm_ar_get_mode; | ||
| 1895 | icm->driver_ready = icm_tr_driver_ready; | ||
| 1896 | icm->device_connected = icm_tr_device_connected; | ||
| 1897 | icm->device_disconnected = icm_tr_device_disconnected; | ||
| 1898 | icm->xdomain_connected = icm_tr_xdomain_connected; | ||
| 1899 | icm->xdomain_disconnected = icm_tr_xdomain_disconnected; | ||
| 1900 | tb->cm_ops = &icm_tr_ops; | ||
| 1295 | break; | 1901 | break; |
| 1296 | } | 1902 | } |
| 1297 | 1903 | ||
| @@ -1036,7 +1036,7 @@ static int nhi_probe(struct pci_dev *pdev, const struct pci_device_id *id) · 1035 unmodified lines | |||
| 1036 | */ | 1036 | */ |
| 1037 | tb_domain_put(tb); | 1037 | tb_domain_put(tb); |
| 1038 | nhi_shutdown(nhi); | 1038 | nhi_shutdown(nhi); |
| 1039 | return -EIO; | 1039 | return res; |
| 1040 | } | 1040 | } |
| 1041 | pci_set_drvdata(pdev, tb); | 1041 | pci_set_drvdata(pdev, tb); |
| 1042 | 1042 | ||
| @@ -1064,6 +1064,7 @@ static const struct dev_pm_ops nhi_pm_ops = { · 21 unmodified lines | |||
| 1064 | * we just disable hotplug, the | 1064 | * we just disable hotplug, the |
| 1065 | * pci-tunnels stay alive. | 1065 | * pci-tunnels stay alive. |
| 1066 | */ | 1066 | */ |
| 1067 | .thaw_noirq = nhi_resume_noirq, | ||
| 1067 | .restore_noirq = nhi_resume_noirq, | 1068 | .restore_noirq = nhi_resume_noirq, |
| 1068 | .suspend = nhi_suspend, | 1069 | .suspend = nhi_suspend, |
| 1069 | .freeze = nhi_suspend, | 1070 | .freeze = nhi_suspend, |
| @@ -1110,6 +1111,8 @@ static struct pci_device_id nhi_ids[] = { · 40 unmodified lines | |||
| 1110 | { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_2C_NHI) }, | 1111 | { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_2C_NHI) }, |
| 1111 | { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_4C_NHI) }, | 1112 | { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_4C_NHI) }, |
| 1112 | { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_USBONLY_NHI) }, | 1113 | { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_USBONLY_NHI) }, |
| 1114 | { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_TITAN_RIDGE_2C_NHI) }, | ||
| 1115 | { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_TITAN_RIDGE_4C_NHI) }, | ||
| 1113 | 1116 | ||
| 1114 | { 0,} | 1117 | { 0,} |
| 1115 | }; | 1118 | }; |
| @@ -45,5 +45,10 @@ enum nhi_fw_mode nhi_mailbox_mode(struct tb_nhi *nhi); · 44 unmodified lines | |||
| 45 | #define PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_LP_USBONLY_NHI 0x15dc | 45 | #define PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_LP_USBONLY_NHI 0x15dc |
| 46 | #define PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_USBONLY_NHI 0x15dd | 46 | #define PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_USBONLY_NHI 0x15dd |
| 47 | #define PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_USBONLY_NHI 0x15de | 47 | #define PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_USBONLY_NHI 0x15de |
| 48 | #define PCI_DEVICE_ID_INTEL_TITAN_RIDGE_2C_BRIDGE 0x15e7 | ||
| 49 | #define PCI_DEVICE_ID_INTEL_TITAN_RIDGE_2C_NHI 0x15e8 | ||
| 50 | #define PCI_DEVICE_ID_INTEL_TITAN_RIDGE_4C_BRIDGE 0x15ea | ||
| 51 | #define PCI_DEVICE_ID_INTEL_TITAN_RIDGE_4C_NHI 0x15eb | ||
| 52 | #define PCI_DEVICE_ID_INTEL_TITAN_RIDGE_DD_BRIDGE 0x15ef | ||
| 48 | 53 | ||
| 49 | #endif | 54 | #endif |
| @@ -716,6 +716,13 @@ static int tb_switch_set_authorized(struct tb_switch *sw, unsigned int val) · 715 unmodified lines | |||
| 716 | if (sw->authorized) | 716 | if (sw->authorized) |
| 717 | goto unlock; | 717 | goto unlock; |
| 718 | 718 | ||
| 719 | /* | ||
| 720 | * Make sure there is no PCIe rescan ongoing when a new PCIe | ||
| 721 | * tunnel is created. Otherwise the PCIe rescan code might find | ||
| 722 | * the new tunnel too early. | ||
| 723 | */ | ||
| 724 | pci_lock_rescan_remove(); | ||
| 725 | |||
| 719 | switch (val) { | 726 | switch (val) { |
| 720 | /* Approve switch */ | 727 | /* Approve switch */ |
| 721 | case 1: | 728 | case 1: |
| @@ -735,6 +742,8 @@ static int tb_switch_set_authorized(struct tb_switch *sw, unsigned int val) · 13 unmodified lines | |||
| 735 | break; | 742 | break; |
| 736 | } | 743 | } |
| 737 | 744 | ||
| 745 | pci_unlock_rescan_remove(); | ||
| 746 | |||
| 738 | if (!ret) { | 747 | if (!ret) { |
| 739 | sw->authorized = val; | 748 | sw->authorized = val; |
| 740 | /* Notify status change to the userspace */ | 749 | /* Notify status change to the userspace */ |
| @@ -766,6 +775,15 @@ static ssize_t authorized_store(struct device *dev, · 25 unmodified lines | |||
| 766 | } | 775 | } |
| 767 | static DEVICE_ATTR_RW(authorized); | 776 | static DEVICE_ATTR_RW(authorized); |
| 768 | 777 | ||
| 778 | static ssize_t boot_show(struct device *dev, struct device_attribute *attr, | ||
| 779 | char *buf) | ||
| 780 | { | ||
| 781 | struct tb_switch *sw = tb_to_switch(dev); | ||
| 782 | |||
| 783 | return sprintf(buf, "%u\n", sw->boot); | ||
| 784 | } | ||
| 785 | static DEVICE_ATTR_RO(boot); | ||
| 786 | |||
| 769 | static ssize_t device_show(struct device *dev, struct device_attribute *attr, | 787 | static ssize_t device_show(struct device *dev, struct device_attribute *attr, |
| 770 | char *buf) | 788 | char *buf) |
| 771 | { | 789 | { |
| @@ -942,6 +960,7 @@ static DEVICE_ATTR_RO(unique_id); · 170 unmodified lines | |||
| 942 | 960 | ||
| 943 | static struct attribute *switch_attrs[] = { | 961 | static struct attribute *switch_attrs[] = { |
| 944 | &dev_attr_authorized.attr, | 962 | &dev_attr_authorized.attr, |
| 963 | &dev_attr_boot.attr, | ||
| 945 | &dev_attr_device.attr, | 964 | &dev_attr_device.attr, |
| 946 | &dev_attr_device_name.attr, | 965 | &dev_attr_device_name.attr, |
| 947 | &dev_attr_key.attr, | 966 | &dev_attr_key.attr, |
| @@ -970,6 +989,10 @@ static umode_t switch_attr_is_visible(struct kobject *kobj, · 22 unmodified lines | |||
| 970 | if (sw->dma_port) | 989 | if (sw->dma_port) |
| 971 | return attr->mode; | 990 | return attr->mode; |
| 972 | return 0; | 991 | return 0; |
| 992 | } else if (attr == &dev_attr_boot.attr) { | ||
| 993 | if (tb_route(sw)) | ||
| 994 | return attr->mode; | ||
| 995 | return 0; | ||
| 973 | } | 996 | } |
| 974 | 997 | ||
| 975 | return sw->safe_mode ? 0 : attr->mode; | 998 | return sw->safe_mode ? 0 : attr->mode; |
| @@ -1028,6 +1051,9 @@ static int tb_switch_get_generation(struct tb_switch *sw) · 52 unmodified lines | |||
| 1028 | case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_4C_BRIDGE: | 1051 | case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_4C_BRIDGE: |
| 1029 | case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_2C_BRIDGE: | 1052 | case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_2C_BRIDGE: |
| 1030 | case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_4C_BRIDGE: | 1053 | case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_4C_BRIDGE: |
| 1054 | case PCI_DEVICE_ID_INTEL_TITAN_RIDGE_2C_BRIDGE: | ||
| 1055 | case PCI_DEVICE_ID_INTEL_TITAN_RIDGE_4C_BRIDGE: | ||
| 1056 | case PCI_DEVICE_ID_INTEL_TITAN_RIDGE_DD_BRIDGE: | ||
| 1031 | return 3; | 1057 | return 3; |
| 1032 | 1058 | ||
| 1033 | default: | 1059 | default: |
| @@ -1470,6 +1496,7 @@ struct tb_sw_lookup { · 436 unmodified lines | |||
| 1470 | u8 link; | 1496 | u8 link; |
| 1471 | u8 depth; | 1497 | u8 depth; |
| 1472 | const uuid_t *uuid; | 1498 | const uuid_t *uuid; |
| 1499 | u64 route; | ||
| 1473 | }; | 1500 | }; |
| 1474 | 1501 | ||
| 1475 | static int tb_switch_match(struct device *dev, void *data) | 1502 | static int tb_switch_match(struct device *dev, void *data) |
| @@ -1485,6 +1512,11 @@ static int tb_switch_match(struct device *dev, void *data) · 9 unmodified lines | |||
| 1485 | if (lookup->uuid) | 1512 | if (lookup->uuid) |
| 1486 | return !memcmp(sw->uuid, lookup->uuid, sizeof(*lookup->uuid)); | 1513 | return !memcmp(sw->uuid, lookup->uuid, sizeof(*lookup->uuid)); |
| 1487 | 1514 | ||
| 1515 | if (lookup->route) { | ||
| 1516 | return sw->config.route_lo == lower_32_bits(lookup->route) && | ||
| 1517 | sw->config.route_hi == upper_32_bits(lookup->route); | ||
| 1518 | } | ||
| 1519 | |||
| 1488 | /* Root switch is matched only by depth */ | 1520 | /* Root switch is matched only by depth */ |
| 1489 | if (!lookup->depth) | 1521 | if (!lookup->depth) |
| 1490 | return !sw->depth; | 1522 | return !sw->depth; |
| @@ -1519,7 +1551,7 @@ struct tb_switch *tb_switch_find_by_link_depth(struct tb *tb, u8 link, u8 depth) · 28 unmodified lines | |||
| 1519 | } | 1551 | } |
| 1520 | 1552 | ||
| 1521 | /** | 1553 | /** |
| 1522 | * tb_switch_find_by_link_depth() - Find switch by UUID | 1554 | * tb_switch_find_by_uuid() - Find switch by UUID |
| 1523 | * @tb: Domain the switch belongs | 1555 | * @tb: Domain the switch belongs |
| 1524 | * @uuid: UUID to look for | 1556 | * @uuid: UUID to look for |
| 1525 | * | 1557 | * |
| @@ -1542,6 +1574,33 @@ struct tb_switch *tb_switch_find_by_uuid(struct tb *tb, const uuid_t *uuid) · 16 unmodified lines | |||
| 1542 | return NULL; | 1574 | return NULL; |
| 1543 | } | 1575 | } |
| 1544 | 1576 | ||
| 1577 | /** | ||
| 1578 | * tb_switch_find_by_route() - Find switch by route string | ||
| 1579 | * @tb: Domain the switch belongs | ||
| 1580 | * @route: Route string to look for | ||
| 1581 | * | ||
| 1582 | * Returned switch has reference count increased so the caller needs to | ||
| 1583 | * call tb_switch_put() when done with the switch. | ||
| 1584 | */ | ||
| 1585 | struct tb_switch *tb_switch_find_by_route(struct tb *tb, u64 route) | ||
| 1586 | { | ||
| 1587 | struct tb_sw_lookup lookup; | ||
| 1588 | struct device *dev; | ||
| 1589 | |||
| 1590 | if (!route) | ||
| 1591 | return tb_switch_get(tb->root_switch); | ||
| 1592 | |||
| 1593 | memset(&lookup, 0, sizeof(lookup)); | ||
| 1594 | lookup.tb = tb; | ||
| 1595 | lookup.route = route; | ||
| 1596 | |||
| 1597 | dev = bus_find_device(&tb_bus_type, NULL, &lookup, tb_switch_match); | ||
| 1598 | if (dev) | ||
| 1599 | return tb_to_switch(dev); | ||
| 1600 | |||
| 1601 | return NULL; | ||
| 1602 | } | ||
| 1603 | |||
| 1545 | void tb_switch_exit(void) | 1604 | void tb_switch_exit(void) |
| 1546 | { | 1605 | { |
| 1547 | ida_destroy(&nvm_ida); | 1606 | ida_destroy(&nvm_ida); |
| @@ -66,6 +66,7 @@ struct tb_switch_nvm { · 65 unmodified lines | |||
| 66 | * @nvm: Pointer to the NVM if the switch has one (%NULL otherwise) | 66 | * @nvm: Pointer to the NVM if the switch has one (%NULL otherwise) |
| 67 | * @no_nvm_upgrade: Prevent NVM upgrade of this switch | 67 | * @no_nvm_upgrade: Prevent NVM upgrade of this switch |
| 68 | * @safe_mode: The switch is in safe-mode | 68 | * @safe_mode: The switch is in safe-mode |
| 69 | * @boot: Whether the switch was already authorized on boot or not | ||
| 69 | * @authorized: Whether the switch is authorized by user or policy | 70 | * @authorized: Whether the switch is authorized by user or policy |
| 70 | * @work: Work used to automatically authorize a switch | 71 | * @work: Work used to automatically authorize a switch |
| 71 | * @security_level: Switch supported security level | 72 | * @security_level: Switch supported security level |
| @@ -99,6 +100,7 @@ struct tb_switch { · 27 unmodified lines | |||
| 99 | struct tb_switch_nvm *nvm; | 100 | struct tb_switch_nvm *nvm; |
| 100 | bool no_nvm_upgrade; | 101 | bool no_nvm_upgrade; |
| 101 | bool safe_mode; | 102 | bool safe_mode; |
| 103 | bool boot; | ||
| 102 | unsigned int authorized; | 104 | unsigned int authorized; |
| 103 | struct work_struct work; | 105 | struct work_struct work; |
| 104 | enum tb_security_level security_level; | 106 | enum tb_security_level security_level; |
| @@ -198,6 +200,8 @@ struct tb_path { · 93 unmodified lines | |||
| 198 | * @suspend: Connection manager specific suspend | 200 | * @suspend: Connection manager specific suspend |
| 199 | * @complete: Connection manager specific complete | 201 | * @complete: Connection manager specific complete |
| 200 | * @handle_event: Handle thunderbolt event | 202 | * @handle_event: Handle thunderbolt event |
| 203 | * @get_boot_acl: Get boot ACL list | ||
| 204 | * @set_boot_acl: Set boot ACL list | ||
| 201 | * @approve_switch: Approve switch | 205 | * @approve_switch: Approve switch |
| 202 | * @add_switch_key: Add key to switch | 206 | * @add_switch_key: Add key to switch |
| 203 | * @challenge_switch_key: Challenge switch using key | 207 | * @challenge_switch_key: Challenge switch using key |
| @@ -215,6 +219,8 @@ struct tb_cm_ops { · 11 unmodified lines | |||
| 215 | void (*complete)(struct tb *tb); | 219 | void (*complete)(struct tb *tb); |
| 216 | void (*handle_event)(struct tb *tb, enum tb_cfg_pkg_type, | 220 | void (*handle_event)(struct tb *tb, enum tb_cfg_pkg_type, |
| 217 | const void *buf, size_t size); | 221 | const void *buf, size_t size); |
| 222 | int (*get_boot_acl)(struct tb *tb, uuid_t *uuids, size_t nuuids); | ||
| 223 | int (*set_boot_acl)(struct tb *tb, const uuid_t *uuids, size_t nuuids); | ||
| 218 | int (*approve_switch)(struct tb *tb, struct tb_switch *sw); | 224 | int (*approve_switch)(struct tb *tb, struct tb_switch *sw); |
| 219 | int (*add_switch_key)(struct tb *tb, struct tb_switch *sw); | 225 | int (*add_switch_key)(struct tb *tb, struct tb_switch *sw); |
| 220 | int (*challenge_switch_key)(struct tb *tb, struct tb_switch *sw, | 226 | int (*challenge_switch_key)(struct tb *tb, struct tb_switch *sw, |
| @@ -386,6 +392,14 @@ struct tb_switch *get_switch_at_route(struct tb_switch *sw, u64 route); · 165 unmodified lines | |||
| 386 | struct tb_switch *tb_switch_find_by_link_depth(struct tb *tb, u8 link, | 392 | struct tb_switch *tb_switch_find_by_link_depth(struct tb *tb, u8 link, |
| 387 | u8 depth); | 393 | u8 depth); |
| 388 | struct tb_switch *tb_switch_find_by_uuid(struct tb *tb, const uuid_t *uuid); | 394 | struct tb_switch *tb_switch_find_by_uuid(struct tb *tb, const uuid_t *uuid); |
| 395 | struct tb_switch *tb_switch_find_by_route(struct tb *tb, u64 route); | ||
| 396 | |||
| 397 | static inline struct tb_switch *tb_switch_get(struct tb_switch *sw) | ||
| 398 | { | ||
| 399 | if (sw) | ||
| 400 | get_device(&sw->dev); | ||
| 401 | return sw; | ||
| 402 | } | ||
| 389 | 403 | ||
| 390 | static inline void tb_switch_put(struct tb_switch *sw) | 404 | static inline void tb_switch_put(struct tb_switch *sw) |
| 391 | { | 405 | { |
| @@ -102,6 +102,8 @@ enum icm_pkg_code { · 101 unmodified lines | |||
| 102 | ICM_ADD_DEVICE_KEY = 0x6, | 102 | ICM_ADD_DEVICE_KEY = 0x6, |
| 103 | ICM_GET_ROUTE = 0xa, | 103 | ICM_GET_ROUTE = 0xa, |
| 104 | ICM_APPROVE_XDOMAIN = 0x10, | 104 | ICM_APPROVE_XDOMAIN = 0x10, |
| 105 | ICM_DISCONNECT_XDOMAIN = 0x11, | ||
| 106 | ICM_PREBOOT_ACL = 0x18, | ||
| 105 | }; | 107 | }; |
| 106 | 108 | ||
| 107 | enum icm_event_code { | 109 | enum icm_event_code { |
| @@ -122,18 +124,23 @@ struct icm_pkg_header { · 14 unmodified lines | |||
| 122 | #define ICM_FLAGS_NO_KEY BIT(1) | 124 | #define ICM_FLAGS_NO_KEY BIT(1) |
| 123 | #define ICM_FLAGS_SLEVEL_SHIFT 3 | 125 | #define ICM_FLAGS_SLEVEL_SHIFT 3 |
| 124 | #define ICM_FLAGS_SLEVEL_MASK GENMASK(4, 3) | 126 | #define ICM_FLAGS_SLEVEL_MASK GENMASK(4, 3) |
| 127 | #define ICM_FLAGS_WRITE BIT(7) | ||
| 125 | 128 | ||
| 126 | struct icm_pkg_driver_ready { | 129 | struct icm_pkg_driver_ready { |
| 127 | struct icm_pkg_header hdr; | 130 | struct icm_pkg_header hdr; |
| 128 | }; | 131 | }; |
| 129 | 132 | ||
| 130 | struct icm_pkg_driver_ready_response { | 133 | /* Falcon Ridge only messages */ |
| 134 | |||
| 135 | struct icm_fr_pkg_driver_ready_response { | ||
| 131 | struct icm_pkg_header hdr; | 136 | struct icm_pkg_header hdr; |
| 132 | u8 romver; | 137 | u8 romver; |
| 133 | u8 ramver; | 138 | u8 ramver; |
| 134 | u16 security_level; | 139 | u16 security_level; |
| 135 | }; | 140 | }; |
| 136 | 141 | ||
| 142 | #define ICM_FR_SLEVEL_MASK 0xf | ||
| 143 | |||
| 137 | /* Falcon Ridge & Alpine Ridge common messages */ | 144 | /* Falcon Ridge & Alpine Ridge common messages */ |
| 138 | 145 | ||
| 139 | struct icm_fr_pkg_get_topology { | 146 | struct icm_fr_pkg_get_topology { |
| @@ -176,6 +183,8 @@ struct icm_fr_event_device_connected { · 36 unmodified lines | |||
| 176 | #define ICM_LINK_INFO_DEPTH_SHIFT 4 | 183 | #define ICM_LINK_INFO_DEPTH_SHIFT 4 |
| 177 | #define ICM_LINK_INFO_DEPTH_MASK GENMASK(7, 4) | 184 | #define ICM_LINK_INFO_DEPTH_MASK GENMASK(7, 4) |
| 178 | #define ICM_LINK_INFO_APPROVED BIT(8) | 185 | #define ICM_LINK_INFO_APPROVED BIT(8) |
| 186 | #define ICM_LINK_INFO_REJECTED BIT(9) | ||
| 187 | #define ICM_LINK_INFO_BOOT BIT(10) | ||
| 179 | 188 | ||
| 180 | struct icm_fr_pkg_approve_device { | 189 | struct icm_fr_pkg_approve_device { |
| 181 | struct icm_pkg_header hdr; | 190 | struct icm_pkg_header hdr; |
| @@ -270,6 +279,18 @@ struct icm_fr_pkg_approve_xdomain_response { · 88 unmodified lines | |||
| 270 | 279 | ||
| 271 | /* Alpine Ridge only messages */ | 280 | /* Alpine Ridge only messages */ |
| 272 | 281 | ||
| 282 | struct icm_ar_pkg_driver_ready_response { | ||
| 283 | struct icm_pkg_header hdr; | ||
| 284 | u8 romver; | ||
| 285 | u8 ramver; | ||
| 286 | u16 info; | ||
| 287 | }; | ||
| 288 | |||
| 289 | #define ICM_AR_INFO_SLEVEL_MASK GENMASK(3, 0) | ||
| 290 | #define ICM_AR_INFO_BOOT_ACL_SHIFT 7 | ||
| 291 | #define ICM_AR_INFO_BOOT_ACL_MASK GENMASK(11, 7) | ||
| 292 | #define ICM_AR_INFO_BOOT_ACL_SUPPORTED BIT(13) | ||
| 293 | |||
| 273 | struct icm_ar_pkg_get_route { | 294 | struct icm_ar_pkg_get_route { |
| 274 | struct icm_pkg_header hdr; | 295 | struct icm_pkg_header hdr; |
| 275 | u16 reserved; | 296 | u16 reserved; |
| @@ -284,6 +305,163 @@ struct icm_ar_pkg_get_route_response { · 8 unmodified lines | |||
| 284 | u32 route_lo; | 305 | u32 route_lo; |
| 285 | }; | 306 | }; |
| 286 | 307 | ||
| 308 | struct icm_ar_boot_acl_entry { | ||
| 309 | u32 uuid_lo; | ||
| 310 | u32 uuid_hi; | ||
| 311 | }; | ||
| 312 | |||
| 313 | #define ICM_AR_PREBOOT_ACL_ENTRIES 16 | ||
| 314 | |||
| 315 | struct icm_ar_pkg_preboot_acl { | ||
| 316 | struct icm_pkg_header hdr; | ||
| 317 | struct icm_ar_boot_acl_entry acl[ICM_AR_PREBOOT_ACL_ENTRIES]; | ||
| 318 | }; | ||
| 319 | |||
| 320 | struct icm_ar_pkg_preboot_acl_response { | ||
| 321 | struct icm_pkg_header hdr; | ||
| 322 | struct icm_ar_boot_acl_entry acl[ICM_AR_PREBOOT_ACL_ENTRIES]; | ||
| 323 | }; | ||
| 324 | |||
| 325 | /* Titan Ridge messages */ | ||
| 326 | |||
| 327 | struct icm_tr_pkg_driver_ready_response { | ||
| 328 | struct icm_pkg_header hdr; | ||
| 329 | u16 reserved1; | ||
| 330 | u16 info; | ||
| 331 | u32 nvm_version; | ||
| 332 | u16 device_id; | ||
| 333 | u16 reserved2; | ||
| 334 | }; | ||
| 335 | |||
| 336 | #define ICM_TR_INFO_SLEVEL_MASK GENMASK(2, 0) | ||
| 337 | #define ICM_TR_INFO_BOOT_ACL_SHIFT 7 | ||
| 338 | #define ICM_TR_INFO_BOOT_ACL_MASK GENMASK(12, 7) | ||
| 339 | |||
| 340 | struct icm_tr_event_device_connected { | ||
| 341 | struct icm_pkg_header hdr; | ||
| 342 | uuid_t ep_uuid; | ||
| 343 | u32 route_hi; | ||
| 344 | u32 route_lo; | ||
| 345 | u8 connection_id; | ||
| 346 | u8 reserved; | ||
| 347 | u16 link_info; | ||
| 348 | u32 ep_name[55]; | ||
| 349 | }; | ||
| 350 | |||
| 351 | struct icm_tr_event_device_disconnected { | ||
| 352 | struct icm_pkg_header hdr; | ||
| 353 | u32 route_hi; | ||
| 354 | u32 route_lo; | ||
| 355 | }; | ||
| 356 | |||
| 357 | struct icm_tr_event_xdomain_connected { | ||
| 358 | struct icm_pkg_header hdr; | ||
| 359 | u16 reserved; | ||
| 360 | u16 link_info; | ||
| 361 | uuid_t remote_uuid; | ||
| 362 | uuid_t local_uuid; | ||
| 363 | u32 local_route_hi; | ||
| 364 | u32 local_route_lo; | ||
| 365 | u32 remote_route_hi; | ||
| 366 | u32 remote_route_lo; | ||
| 367 | }; | ||
| 368 | |||
| 369 | struct icm_tr_event_xdomain_disconnected { | ||
| 370 | struct icm_pkg_header hdr; | ||
| 371 | u32 route_hi; | ||
| 372 | u32 route_lo; | ||
| 373 | uuid_t remote_uuid; | ||
| 374 | }; | ||
| 375 | |||
| 376 | struct icm_tr_pkg_approve_device { | ||
| 377 | struct icm_pkg_header hdr; | ||
| 378 | uuid_t ep_uuid; | ||
| 379 | u32 route_hi; | ||
| 380 | u32 route_lo; | ||
| 381 | u8 connection_id; | ||
| 382 | u8 reserved1[3]; | ||
| 383 | }; | ||
| 384 | |||
| 385 | struct icm_tr_pkg_add_device_key { | ||
| 386 | struct icm_pkg_header hdr; | ||
| 387 | uuid_t ep_uuid; | ||
| 388 | u32 route_hi; | ||
| 389 | u32 route_lo; | ||
| 390 | u8 connection_id; | ||
| 391 | u8 reserved[3]; | ||
| 392 | u32 key[8]; | ||
| 393 | }; | ||
| 394 | |||
| 395 | struct icm_tr_pkg_challenge_device { | ||
| 396 | struct icm_pkg_header hdr; | ||
| 397 | uuid_t ep_uuid; | ||
| 398 | u32 route_hi; | ||
| 399 | u32 route_lo; | ||
| 400 | u8 connection_id; | ||
| 401 | u8 reserved[3]; | ||
| 402 | u32 challenge[8]; | ||
| 403 | }; | ||
| 404 | |||
| 405 | struct icm_tr_pkg_approve_xdomain { | ||
| 406 | struct icm_pkg_header hdr; | ||
| 407 | u32 route_hi; | ||
| 408 | u32 route_lo; | ||
| 409 | uuid_t remote_uuid; | ||
| 410 | u16 transmit_path; | ||
| 411 | u16 transmit_ring; | ||
| 412 | u16 receive_path; | ||
| 413 | u16 receive_ring; | ||
| 414 | }; | ||
| 415 | |||
| 416 | struct icm_tr_pkg_disconnect_xdomain { | ||
| 417 | struct icm_pkg_header hdr; | ||
| 418 | u8 stage; | ||
| 419 | u8 reserved[3]; | ||
| 420 | u32 route_hi; | ||
| 421 | u32 route_lo; | ||
| 422 | uuid_t remote_uuid; | ||
| 423 | }; | ||
| 424 | |||
| 425 | struct icm_tr_pkg_challenge_device_response { | ||
| 426 | struct icm_pkg_header hdr; | ||
| 427 | uuid_t ep_uuid; | ||
| 428 | u32 route_hi; | ||
| 429 | u32 route_lo; | ||
| 430 | u8 connection_id; | ||
| 431 | u8 reserved[3]; | ||
| 432 | u32 challenge[8]; | ||
| 433 | u32 response[8]; | ||
| 434 | }; | ||
| 435 | |||
| 436 | struct icm_tr_pkg_add_device_key_response { | ||
| 437 | struct icm_pkg_header hdr; | ||
| 438 | uuid_t ep_uuid; | ||
| 439 | u32 route_hi; | ||
| 440 | u32 route_lo; | ||
| 441 | u8 connection_id; | ||
| 442 | u8 reserved[3]; | ||
| 443 | }; | ||
| 444 | |||
| 445 | struct icm_tr_pkg_approve_xdomain_response { | ||
| 446 | struct icm_pkg_header hdr; | ||
| 447 | u32 route_hi; | ||
| 448 | u32 route_lo; | ||
| 449 | uuid_t remote_uuid; | ||
| 450 | u16 transmit_path; | ||
| 451 | u16 transmit_ring; | ||
| 452 | u16 receive_path; | ||
| 453 | u16 receive_ring; | ||
| 454 | }; | ||
| 455 | |||
| 456 | struct icm_tr_pkg_disconnect_xdomain_response { | ||
| 457 | struct icm_pkg_header hdr; | ||
| 458 | u8 stage; | ||
| 459 | u8 reserved[3]; | ||
| 460 | u32 route_hi; | ||
| 461 | u32 route_lo; | ||
| 462 | uuid_t remote_uuid; | ||
| 463 | }; | ||
| 464 | |||
| 287 | /* XDomain messages */ | 465 | /* XDomain messages */ |
| 288 | 466 | ||
| 289 | struct tb_xdomain_header { | 467 | struct tb_xdomain_header { |
| @@ -1255,6 +1255,7 @@ struct tb_xdomain_lookup { · 1254 unmodified lines | |||
| 1255 | const uuid_t *uuid; | 1255 | const uuid_t *uuid; |
| 1256 | u8 link; | 1256 | u8 link; |
| 1257 | u8 depth; | 1257 | u8 depth; |
| 1258 | u64 route; | ||
| 1258 | }; | 1259 | }; |
| 1259 | 1260 | ||
| 1260 | static struct tb_xdomain *switch_find_xdomain(struct tb_switch *sw, | 1261 | static struct tb_xdomain *switch_find_xdomain(struct tb_switch *sw, |
| @@ -1275,9 +1276,13 @@ static struct tb_xdomain *switch_find_xdomain(struct tb_switch *sw, · 14 unmodified lines | |||
| 1275 | if (lookup->uuid) { | 1276 | if (lookup->uuid) { |
| 1276 | if (uuid_equal(xd->remote_uuid, lookup->uuid)) | 1277 | if (uuid_equal(xd->remote_uuid, lookup->uuid)) |
| 1277 | return xd; | 1278 | return xd; |
| 1278 | } else if (lookup->link == xd->link && | 1279 | } else if (lookup->link && |
| 1280 | lookup->link == xd->link && | ||
| 1279 | lookup->depth == xd->depth) { | 1281 | lookup->depth == xd->depth) { |
| 1280 | return xd; | 1282 | return xd; |
| 1283 | } else if (lookup->route && | ||
| 1284 | lookup->route == xd->route) { | ||
| 1285 | return xd; | ||
| 1281 | } | 1286 | } |
| 1282 | } else if (port->remote) { | 1287 | } else if (port->remote) { |
| 1283 | xd = switch_find_xdomain(port->remote->sw, lookup); | 1288 | xd = switch_find_xdomain(port->remote->sw, lookup); |
| @@ -1313,12 +1318,7 @@ struct tb_xdomain *tb_xdomain_find_by_uuid(struct tb *tb, const uuid_t *uuid) · 29 unmodified lines | |||
| 1313 | lookup.uuid = uuid; | 1318 | lookup.uuid = uuid; |
| 1314 | 1319 | ||
| 1315 | xd = switch_find_xdomain(tb->root_switch, &lookup); | 1320 | xd = switch_find_xdomain(tb->root_switch, &lookup); |
| 1316 | if (xd) { | 1321 | return tb_xdomain_get(xd); |
| 1317 | get_device(&xd->dev); | ||
| 1318 | return xd; | ||
| 1319 | } | ||
| 1320 | |||
| 1321 | return NULL; | ||
| 1322 | } | 1322 | } |
| 1323 | EXPORT_SYMBOL_GPL(tb_xdomain_find_by_uuid); | 1323 | EXPORT_SYMBOL_GPL(tb_xdomain_find_by_uuid); |
| 1324 | 1324 | ||
| @@ -1349,13 +1349,36 @@ struct tb_xdomain *tb_xdomain_find_by_link_depth(struct tb *tb, u8 link, · 24 unmodified lines | |||
| 1349 | lookup.depth = depth; | 1349 | lookup.depth = depth; |
| 1350 | 1350 | ||
| 1351 | xd = switch_find_xdomain(tb->root_switch, &lookup); | 1351 | xd = switch_find_xdomain(tb->root_switch, &lookup); |
| 1352 | if (xd) { | 1352 | return tb_xdomain_get(xd); |
| 1353 | get_device(&xd->dev); | 1353 | } |
| 1354 | return xd; | ||
| 1355 | } | ||
| 1356 | 1354 | ||
| 1357 | return NULL; | 1355 | /** |
| 1356 | * tb_xdomain_find_by_route() - Find an XDomain by route string | ||
| 1357 | * @tb: Domain where the XDomain belongs to | ||
| 1358 | * @route: XDomain route string | ||
| 1359 | * | ||
| 1360 | * Finds XDomain by walking through the Thunderbolt topology below @tb. | ||
| 1361 | * The returned XDomain will have its reference count increased so the | ||
| 1362 | * caller needs to call tb_xdomain_put() when it is done with the | ||
| 1363 | * object. | ||
| 1364 | * | ||
| 1365 | * This will find all XDomains including the ones that are not yet added | ||
| 1366 | * to the bus (handshake is still in progress). | ||
| 1367 | * | ||
| 1368 | * The caller needs to hold @tb->lock. | ||
| 1369 | */ | ||
| 1370 | struct tb_xdomain *tb_xdomain_find_by_route(struct tb *tb, u64 route) | ||
| 1371 | { | ||
| 1372 | struct tb_xdomain_lookup lookup; | ||
| 1373 | struct tb_xdomain *xd; | ||
| 1374 | |||
| 1375 | memset(&lookup, 0, sizeof(lookup)); | ||
| 1376 | lookup.route = route; | ||
| 1377 | |||
| 1378 | xd = switch_find_xdomain(tb->root_switch, &lookup); | ||
| 1379 | return tb_xdomain_get(xd); | ||
| 1358 | } | 1380 | } |
| 1381 | EXPORT_SYMBOL_GPL(tb_xdomain_find_by_route); | ||
| 1359 | 1382 | ||
| 1360 | bool tb_xdomain_handle_request(struct tb *tb, enum tb_cfg_pkg_type type, | 1383 | bool tb_xdomain_handle_request(struct tb *tb, enum tb_cfg_pkg_type type, |
| 1361 | const void *buf, size_t size) | 1384 | const void *buf, size_t size) |
| @@ -121,6 +121,94 @@ static void hv_uio_rescind(struct vmbus_channel *channel) · 120 unmodified lines | |||
| 121 | uio_event_notify(&pdata->info); | 121 | uio_event_notify(&pdata->info); |
| 122 | } | 122 | } |
| 123 | 123 | ||
| 124 | /* | ||
| 125 | * Handle fault when looking for sub channel ring buffer | ||
| 126 | * Subchannel ring buffer is same as resource 0 which is main ring buffer | ||
| 127 | * This is derived from uio_vma_fault | ||
| 128 | */ | ||
| 129 | static int hv_uio_vma_fault(struct vm_fault *vmf) | ||
| 130 | { | ||
| 131 | struct vm_area_struct *vma = vmf->vma; | ||
| 132 | void *ring_buffer = vma->vm_private_data; | ||
| 133 | struct page *page; | ||
| 134 | void *addr; | ||
| 135 | |||
| 136 | addr = ring_buffer + (vmf->pgoff << PAGE_SHIFT); | ||
| 137 | page = virt_to_page(addr); | ||
| 138 | get_page(page); | ||
| 139 | vmf->page = page; | ||
| 140 | return 0; | ||
| 141 | } | ||
| 142 | |||
| 143 | static const struct vm_operations_struct hv_uio_vm_ops = { | ||
| 144 | .fault = hv_uio_vma_fault, | ||
| 145 | }; | ||
| 146 | |||
| 147 | /* Sysfs API to allow mmap of the ring buffers */ | ||
| 148 | static int hv_uio_ring_mmap(struct file *filp, struct kobject *kobj, | ||
| 149 | struct bin_attribute *attr, | ||
| 150 | struct vm_area_struct *vma) | ||
| 151 | { | ||
| 152 | struct vmbus_channel *channel | ||
| 153 | = container_of(kobj, struct vmbus_channel, kobj); | ||
| 154 | unsigned long requested_pages, actual_pages; | ||
| 155 | |||
| 156 | if (vma->vm_end < vma->vm_start) | ||
| 157 | return -EINVAL; | ||
| 158 | |||
| 159 | /* only allow 0 for now */ | ||
| 160 | if (vma->vm_pgoff > 0) | ||
| 161 | return -EINVAL; | ||
| 162 | |||
| 163 | requested_pages = vma_pages(vma); | ||
| 164 | actual_pages = 2 * HV_RING_SIZE; | ||
| 165 | if (requested_pages > actual_pages) | ||
| 166 | return -EINVAL; | ||
| 167 | |||
| 168 | vma->vm_private_data = channel->ringbuffer_pages; | ||
| 169 | vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP; | ||
| 170 | vma->vm_ops = &hv_uio_vm_ops; | ||
| 171 | return 0; | ||
| 172 | } | ||
| 173 | |||
| 174 | static struct bin_attribute ring_buffer_bin_attr __ro_after_init = { | ||
| 175 | .attr = { | ||
| 176 | .name = "ring", | ||
| 177 | .mode = 0600, | ||
| 178 | /* size is set at init time */ | ||
| 179 | }, | ||
| 180 | .mmap = hv_uio_ring_mmap, | ||
| 181 | }; | ||
| 182 | |||
| 183 | /* Callback from VMBUS subystem when new channel created. */ | ||
| 184 | static void | ||
| 185 | hv_uio_new_channel(struct vmbus_channel *new_sc) | ||
| 186 | { | ||
| 187 | struct hv_device *hv_dev = new_sc->primary_channel->device_obj; | ||
| 188 | struct device *device = &hv_dev->device; | ||
| 189 | struct hv_uio_private_data *pdata = hv_get_drvdata(hv_dev); | ||
| 190 | const size_t ring_bytes = HV_RING_SIZE * PAGE_SIZE; | ||
| 191 | int ret; | ||
| 192 | |||
| 193 | /* Create host communication ring */ | ||
| 194 | ret = vmbus_open(new_sc, ring_bytes, ring_bytes, NULL, 0, | ||
| 195 | hv_uio_channel_cb, pdata); | ||
| 196 | if (ret) { | ||
| 197 | dev_err(device, "vmbus_open subchannel failed: %d\n", ret); | ||
| 198 | return; | ||
| 199 | } | ||
| 200 | |||
| 201 | /* Disable interrupts on sub channel */ | ||
| 202 | new_sc->inbound.ring_buffer->interrupt_mask = 1; | ||
| 203 | set_channel_read_mode(new_sc, HV_CALL_ISR); | ||
| 204 | |||
| 205 | ret = sysfs_create_bin_file(&new_sc->kobj, &ring_buffer_bin_attr); | ||
| 206 | if (ret) { | ||
| 207 | dev_err(device, "sysfs create ring bin file failed; %d\n", ret); | ||
| 208 | vmbus_close(new_sc); | ||
| 209 | } | ||
| 210 | } | ||
| 211 | |||
| 124 | static void | 212 | static void |
| 125 | hv_uio_cleanup(struct hv_device *dev, struct hv_uio_private_data *pdata) | 213 | hv_uio_cleanup(struct hv_device *dev, struct hv_uio_private_data *pdata) |
| 126 | { | 214 | { |
| @@ -236,6 +324,7 @@ hv_uio_probe(struct hv_device *dev, · 109 unmodified lines | |||
| 236 | } | 324 | } |
| 237 | 325 | ||
| 238 | vmbus_set_chn_rescind_callback(dev->channel, hv_uio_rescind); | 326 | vmbus_set_chn_rescind_callback(dev->channel, hv_uio_rescind); |
| 327 | vmbus_set_sc_create_callback(dev->channel, hv_uio_new_channel); | ||
| 239 | 328 | ||
| 240 | hv_set_drvdata(dev, pdata); | 329 | hv_set_drvdata(dev, pdata); |
| 241 | 330 | ||
| @@ -706,6 +706,7 @@ static int __w1_attach_slave_device(struct w1_slave *sl) · 705 unmodified lines | |||
| 706 | dev_err(&sl->dev, | 706 | dev_err(&sl->dev, |
| 707 | "Device registration [%s] failed. err=%d\n", | 707 | "Device registration [%s] failed. err=%d\n", |
| 708 | dev_name(&sl->dev), err); | 708 | dev_name(&sl->dev), err); |
| 709 | put_device(&sl->dev); | ||
| 709 | return err; | 710 | return err; |
| 710 | } | 711 | } |
| 711 | w1_family_notify(BUS_NOTIFY_ADD_DEVICE, sl); | 712 | w1_family_notify(BUS_NOTIFY_ADD_DEVICE, sl); |
| @@ -67,18 +67,18 @@ static int find_dynamic_major(void) · 66 unmodified lines | |||
| 67 | int i; | 67 | int i; |
| 68 | struct char_device_struct *cd; | 68 | struct char_device_struct *cd; |
| 69 | 69 | ||
| 70 | for (i = ARRAY_SIZE(chrdevs)-1; i > CHRDEV_MAJOR_DYN_END; i--) { | 70 | for (i = ARRAY_SIZE(chrdevs)-1; i >= CHRDEV_MAJOR_DYN_END; i--) { |
| 71 | if (chrdevs[i] == NULL) | 71 | if (chrdevs[i] == NULL) |
| 72 | return i; | 72 | return i; |
| 73 | } | 73 | } |
| 74 | 74 | ||
| 75 | for (i = CHRDEV_MAJOR_DYN_EXT_START; | 75 | for (i = CHRDEV_MAJOR_DYN_EXT_START; |
| 76 | i > CHRDEV_MAJOR_DYN_EXT_END; i--) { | 76 | i >= CHRDEV_MAJOR_DYN_EXT_END; i--) { |
| 77 | for (cd = chrdevs[major_to_index(i)]; cd; cd = cd->next) | 77 | for (cd = chrdevs[major_to_index(i)]; cd; cd = cd->next) |
| 78 | if (cd->major == i) | 78 | if (cd->major == i) |
| 79 | break; | 79 | break; |
| 80 | 80 | ||
| 81 | if (cd == NULL || cd->major != i) | 81 | if (cd == NULL) |
| 82 | return i; | 82 | return i; |
| 83 | } | 83 | } |
| 84 | 84 | ||
| @@ -121,8 +121,8 @@ __register_chrdev_region(unsigned int major, unsigned int baseminor, · 36 unmodified lines | |||
| 121 | } | 121 | } |
| 122 | 122 | ||
| 123 | if (major >= CHRDEV_MAJOR_MAX) { | 123 | if (major >= CHRDEV_MAJOR_MAX) { |
| 124 | pr_err("CHRDEV \"%s\" major requested (%d) is greater than the maximum (%d)\n", | 124 | pr_err("CHRDEV \"%s\" major requested (%u) is greater than the maximum (%u)\n", |
| 125 | name, major, CHRDEV_MAJOR_MAX); | 125 | name, major, CHRDEV_MAJOR_MAX-1); |
| 126 | ret = -EINVAL; | 126 | ret = -EINVAL; |
| 127 | goto out; | 127 | goto out; |
| 128 | } | 128 | } |
| @@ -230,6 +230,7 @@ extern void devm_extcon_unregister_notifier_all(struct device *dev, · 229 unmodified lines | |||
| 230 | * Following APIs get the extcon_dev from devicetree or by through extcon name. | 230 | * Following APIs get the extcon_dev from devicetree or by through extcon name. |
| 231 | */ | 231 | */ |
| 232 | extern struct extcon_dev *extcon_get_extcon_dev(const char *extcon_name); | 232 | extern struct extcon_dev *extcon_get_extcon_dev(const char *extcon_name); |
| 233 | extern struct extcon_dev *extcon_find_edev_by_node(struct device_node *node); | ||
| 233 | extern struct extcon_dev *extcon_get_edev_by_phandle(struct device *dev, | 234 | extern struct extcon_dev *extcon_get_edev_by_phandle(struct device *dev, |
| 234 | int index); | 235 | int index); |
| 235 | 236 | ||
| @@ -283,6 +284,11 @@ static inline struct extcon_dev *extcon_get_extcon_dev(const char *extcon_name) · 47 unmodified lines | |||
| 283 | return ERR_PTR(-ENODEV); | 284 | return ERR_PTR(-ENODEV); |
| 284 | } | 285 | } |
| 285 | 286 | ||
| 287 | static inline struct extcon_dev *extcon_find_edev_by_node(struct device_node *node) | ||
| 288 | { | ||
| 289 | return ERR_PTR(-ENODEV); | ||
| 290 | } | ||
| 291 | |||
| 286 | static inline struct extcon_dev *extcon_get_edev_by_phandle(struct device *dev, | 292 | static inline struct extcon_dev *extcon_get_edev_by_phandle(struct device *dev, |
| 287 | int index) | 293 | int index) |
| 288 | { | 294 | { |
| @@ -1,47 +0,0 @@ | |||
| 1 | /* | ||
| 2 | * Single-state GPIO extcon driver based on extcon class | ||
| 3 | * | ||
| 4 | * Copyright (C) 2012 Samsung Electronics | ||
| 5 | * Author: MyungJoo Ham <myungjoo.ham@samsung.com> | ||
| 6 | * | ||
| 7 | * based on switch class driver | ||
| 8 | * Copyright (C) 2008 Google, Inc. | ||
| 9 | * Author: Mike Lockwood <lockwood@android.com> | ||
| 10 | * | ||
| 11 | * This software is licensed under the terms of the GNU General Public | ||
| 12 | * License version 2, as published by the Free Software Foundation, and | ||
| 13 | * may be copied, distributed, and modified under those terms. | ||
| 14 | * | ||
| 15 | * This program is distributed in the hope that it will be useful, | ||
| 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 18 | * GNU General Public License for more details. | ||
| 19 | */ | ||
| 20 | #ifndef __EXTCON_GPIO_H__ | ||
| 21 | #define __EXTCON_GPIO_H__ __FILE__ | ||
| 22 | |||
| 23 | #include <linux/extcon.h> | ||
| 24 | |||
| 25 | /** | ||
| 26 | * struct gpio_extcon_pdata - A simple GPIO-controlled extcon device. | ||
| 27 | * @extcon_id: The unique id of specific external connector. | ||
| 28 | * @gpio: Corresponding GPIO. | ||
| 29 | * @gpio_active_low: Boolean describing whether gpio active state is 1 or 0 | ||
| 30 | * If true, low state of gpio means active. | ||
| 31 | * If false, high state of gpio means active. | ||
| 32 | * @debounce: Debounce time for GPIO IRQ in ms. | ||
| 33 | * @irq_flags: IRQ Flags (e.g., IRQF_TRIGGER_LOW). | ||
| 34 | * @check_on_resume: Boolean describing whether to check the state of gpio | ||
| 35 | * while resuming from sleep. | ||
| 36 | */ | ||
| 37 | struct gpio_extcon_pdata { | ||
| 38 | unsigned int extcon_id; | ||
| 39 | unsigned gpio; | ||
| 40 | bool gpio_active_low; | ||
| 41 | unsigned long debounce; | ||
| 42 | unsigned long irq_flags; | ||
| 43 | |||
| 44 | bool check_on_resume; | ||
| 45 | }; | ||
| 46 | |||
| 47 | #endif /* __EXTCON_GPIO_H__ */ | ||
| @@ -844,7 +844,7 @@ struct vmbus_channel { · 843 unmodified lines | |||
| 844 | 844 | ||
| 845 | /* | 845 | /* |
| 846 | * NUMA distribution policy: | 846 | * NUMA distribution policy: |
| 847 | * We support teo policies: | 847 | * We support two policies: |
| 848 | * 1) Balanced: Here all performance critical channels are | 848 | * 1) Balanced: Here all performance critical channels are |
| 849 | * distributed evenly amongst all the NUMA nodes. | 849 | * distributed evenly amongst all the NUMA nodes. |
| 850 | * This policy will be the default policy. | 850 | * This policy will be the default policy. |
| @@ -22,6 +22,31 @@ typedef int (*nvmem_reg_read_t)(void *priv, unsigned int offset, · 21 unmodified lines | |||
| 22 | typedef int (*nvmem_reg_write_t)(void *priv, unsigned int offset, | 22 | typedef int (*nvmem_reg_write_t)(void *priv, unsigned int offset, |
| 23 | void *val, size_t bytes); | 23 | void *val, size_t bytes); |
| 24 | 24 | ||
| 25 | /** | ||
| 26 | * struct nvmem_config - NVMEM device configuration | ||
| 27 | * | ||
| 28 | * @dev: Parent device. | ||
| 29 | * @name: Optional name. | ||
| 30 | * @id: Optional device ID used in full name. Ignored if name is NULL. | ||
| 31 | * @owner: Pointer to exporter module. Used for refcounting. | ||
| 32 | * @cells: Optional array of pre-defined NVMEM cells. | ||
| 33 | * @ncells: Number of elements in cells. | ||
| 34 | * @read_only: Device is read-only. | ||
| 35 | * @root_only: Device is accessibly to root only. | ||
| 36 | * @reg_read: Callback to read data. | ||
| 37 | * @reg_write: Callback to write data. | ||
| 38 | * @size: Device size. | ||
| 39 | * @word_size: Minimum read/write access granularity. | ||
| 40 | * @stride: Minimum read/write access stride. | ||
| 41 | * @priv: User context passed to read/write callbacks. | ||
| 42 | * | ||
| 43 | * Note: A default "nvmem<id>" name will be assigned to the device if | ||
| 44 | * no name is specified in its configuration. In such case "<id>" is | ||
| 45 | * generated with ida_simple_get() and provided id field is ignored. | ||
| 46 | * | ||
| 47 | * Note: Specifying name and setting id to -1 implies a unique device | ||
| 48 | * whose name is provided as-is (kept unaltered). | ||
| 49 | */ | ||
| 25 | struct nvmem_config { | 50 | struct nvmem_config { |
| 26 | struct device *dev; | 51 | struct device *dev; |
| 27 | const char *name; | 52 | const char *name; |
| @@ -47,6 +72,11 @@ struct nvmem_config { · 19 unmodified lines | |||
| 47 | struct nvmem_device *nvmem_register(const struct nvmem_config *cfg); | 72 | struct nvmem_device *nvmem_register(const struct nvmem_config *cfg); |
| 48 | int nvmem_unregister(struct nvmem_device *nvmem); | 73 | int nvmem_unregister(struct nvmem_device *nvmem); |
| 49 | 74 | ||
| 75 | struct nvmem_device *devm_nvmem_register(struct device *dev, | ||
| 76 | const struct nvmem_config *cfg); | ||
| 77 | |||
| 78 | int devm_nvmem_unregister(struct device *dev, struct nvmem_device *nvmem); | ||
| 79 | |||
| 50 | #else | 80 | #else |
| 51 | 81 | ||
| 52 | static inline struct nvmem_device *nvmem_register(const struct nvmem_config *c) | 82 | static inline struct nvmem_device *nvmem_register(const struct nvmem_config *c) |
| @@ -59,5 +89,17 @@ static inline int nvmem_unregister(struct nvmem_device *nvmem) · 6 unmodified lines | |||
| 59 | return -ENOSYS; | 89 | return -ENOSYS; |
| 60 | } | 90 | } |
| 61 | 91 | ||
| 92 | static inline struct nvmem_device * | ||
| 93 | devm_nvmem_register(struct device *dev, const struct nvmem_config *c) | ||
| 94 | { | ||
| 95 | return nvmem_register(c); | ||
| 96 | } | ||
| 97 | |||
| 98 | static inline int | ||
| 99 | devm_nvmem_unregister(struct device *dev, struct nvmem_device *nvmem) | ||
| 100 | { | ||
| 101 | return nvmem_unregister(nvmem); | ||
| 102 | } | ||
| 103 | |||
| 62 | #endif /* CONFIG_NVMEM */ | 104 | #endif /* CONFIG_NVMEM */ |
| 63 | #endif /* ifndef _LINUX_NVMEM_PROVIDER_H */ | 105 | #endif /* ifndef _LINUX_NVMEM_PROVIDER_H */ |
| @@ -1562,6 +1562,8 @@ · 1561 unmodified lines | |||
| 1562 | #define PCI_DEVICE_ID_SERVERWORKS_CSB6LPC 0x0227 | 1562 | #define PCI_DEVICE_ID_SERVERWORKS_CSB6LPC 0x0227 |
| 1563 | #define PCI_DEVICE_ID_SERVERWORKS_HT1100LD 0x0408 | 1563 | #define PCI_DEVICE_ID_SERVERWORKS_HT1100LD 0x0408 |
| 1564 | 1564 | ||
| 1565 | #define PCI_VENDOR_ID_ALTERA 0x1172 | ||
| 1566 | |||
| 1565 | #define PCI_VENDOR_ID_SBE 0x1176 | 1567 | #define PCI_VENDOR_ID_SBE 0x1176 |
| 1566 | #define PCI_DEVICE_ID_SBE_WANXL100 0x0301 | 1568 | #define PCI_DEVICE_ID_SBE_WANXL100 0x0301 |
| 1567 | #define PCI_DEVICE_ID_SBE_WANXL200 0x0302 | 1569 | #define PCI_DEVICE_ID_SBE_WANXL200 0x0302 |
| @@ -1,15 +1,7 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 1 | /* | 2 | /* |
| 2 | * System Trace Module (STM) infrastructure apis | 3 | * System Trace Module (STM) infrastructure apis |
| 3 | * Copyright (C) 2014 Intel Corporation. | 4 | * Copyright (C) 2014 Intel Corporation. |
| 4 | * | ||
| 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 | */ | 5 | */ |
| 14 | 6 | ||
| 15 | #ifndef _STM_H_ | 7 | #ifndef _STM_H_ |
| @@ -45,12 +45,16 @@ enum tb_cfg_pkg_type { · 44 unmodified lines | |||
| 45 | * @TB_SECURITY_USER: User approval required at minimum | 45 | * @TB_SECURITY_USER: User approval required at minimum |
| 46 | * @TB_SECURITY_SECURE: One time saved key required at minimum | 46 | * @TB_SECURITY_SECURE: One time saved key required at minimum |
| 47 | * @TB_SECURITY_DPONLY: Only tunnel Display port (and USB) | 47 | * @TB_SECURITY_DPONLY: Only tunnel Display port (and USB) |
| 48 | * @TB_SECURITY_USBONLY: Only tunnel USB controller of the connected | ||
| 49 | * Thunderbolt dock (and Display Port). All PCIe | ||
| 50 | * links downstream of the dock are removed. | ||
| 48 | */ | 51 | */ |
| 49 | enum tb_security_level { | 52 | enum tb_security_level { |
| 50 | TB_SECURITY_NONE, | 53 | TB_SECURITY_NONE, |
| 51 | TB_SECURITY_USER, | 54 | TB_SECURITY_USER, |
| 52 | TB_SECURITY_SECURE, | 55 | TB_SECURITY_SECURE, |
| 53 | TB_SECURITY_DPONLY, | 56 | TB_SECURITY_DPONLY, |
| 57 | TB_SECURITY_USBONLY, | ||
| 54 | }; | 58 | }; |
| 55 | 59 | ||
| 56 | /** | 60 | /** |
| @@ -65,6 +69,7 @@ enum tb_security_level { · 8 unmodified lines | |||
| 65 | * @cm_ops: Connection manager specific operations vector | 69 | * @cm_ops: Connection manager specific operations vector |
| 66 | * @index: Linux assigned domain number | 70 | * @index: Linux assigned domain number |
| 67 | * @security_level: Current security level | 71 | * @security_level: Current security level |
| 72 | * @nboot_acl: Number of boot ACLs the domain supports | ||
| 68 | * @privdata: Private connection manager specific data | 73 | * @privdata: Private connection manager specific data |
| 69 | */ | 74 | */ |
| 70 | struct tb { | 75 | struct tb { |
| @@ -77,6 +82,7 @@ struct tb { · 6 unmodified lines | |||
| 77 | const struct tb_cm_ops *cm_ops; | 82 | const struct tb_cm_ops *cm_ops; |
| 78 | int index; | 83 | int index; |
| 79 | enum tb_security_level security_level; | 84 | enum tb_security_level security_level; |
| 85 | size_t nboot_acl; | ||
| 80 | unsigned long privdata[0]; | 86 | unsigned long privdata[0]; |
| 81 | }; | 87 | }; |
| 82 | 88 | ||
| @@ -237,6 +243,7 @@ int tb_xdomain_enable_paths(struct tb_xdomain *xd, u16 transmit_path, · 154 unmodified lines | |||
| 237 | u16 receive_ring); | 243 | u16 receive_ring); |
| 238 | int tb_xdomain_disable_paths(struct tb_xdomain *xd); | 244 | int tb_xdomain_disable_paths(struct tb_xdomain *xd); |
| 239 | struct tb_xdomain *tb_xdomain_find_by_uuid(struct tb *tb, const uuid_t *uuid); | 245 | struct tb_xdomain *tb_xdomain_find_by_uuid(struct tb *tb, const uuid_t *uuid); |
| 246 | struct tb_xdomain *tb_xdomain_find_by_route(struct tb *tb, u64 route); | ||
| 240 | 247 | ||
| 241 | static inline struct tb_xdomain * | 248 | static inline struct tb_xdomain * |
| 242 | tb_xdomain_find_by_uuid_locked(struct tb *tb, const uuid_t *uuid) | 249 | tb_xdomain_find_by_uuid_locked(struct tb *tb, const uuid_t *uuid) |
| @@ -250,6 +257,18 @@ tb_xdomain_find_by_uuid_locked(struct tb *tb, const uuid_t *uuid) · 7 unmodified lines | |||
| 250 | return xd; | 257 | return xd; |
| 251 | } | 258 | } |
| 252 | 259 | ||
| 260 | static inline struct tb_xdomain * | ||
| 261 | tb_xdomain_find_by_route_locked(struct tb *tb, u64 route) | ||
| 262 | { | ||
| 263 | struct tb_xdomain *xd; | ||
| 264 | |||
| 265 | mutex_lock(&tb->lock); | ||
| 266 | xd = tb_xdomain_find_by_route(tb, route); | ||
| 267 | mutex_unlock(&tb->lock); | ||
| 268 | |||
| 269 | return xd; | ||
| 270 | } | ||
| 271 | |||
| 253 | static inline struct tb_xdomain *tb_xdomain_get(struct tb_xdomain *xd) | 272 | static inline struct tb_xdomain *tb_xdomain_get(struct tb_xdomain *xd) |
| 254 | { | 273 | { |
| 255 | if (xd) | 274 | if (xd) |
| @@ -3,15 +3,6 @@ · 2 unmodified lines | |||
| 3 | * System Trace Module (STM) userspace interfaces | 3 | * System Trace Module (STM) userspace interfaces |
| 4 | * Copyright (c) 2014, Intel Corporation. | 4 | * Copyright (c) 2014, Intel Corporation. |
| 5 | * | 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 | * STM class implements generic infrastructure for System Trace Module devices | 6 | * STM class implements generic infrastructure for System Trace Module devices |
| 16 | * as defined in MIPI STPv2 specification. | 7 | * as defined in MIPI STPv2 specification. |
| 17 | */ | 8 | */ |
| @@ -21,6 +12,10 @@ · 3 unmodified lines | |||
| 21 | 12 | ||
| 22 | #include <linux/types.h> | 13 | #include <linux/types.h> |
| 23 | 14 | ||
| 15 | /* Maximum allowed master and channel values */ | ||
| 16 | #define STP_MASTER_MAX 0xffff | ||
| 17 | #define STP_CHANNEL_MAX 0xffff | ||
| 18 | |||
| 24 | /** | 19 | /** |
| 25 | * struct stp_policy_id - identification for the STP policy | 20 | * struct stp_policy_id - identification for the STP policy |
| 26 | * @size: size of the structure including real id[] length | 21 | * @size: size of the structure including real id[] length |
| @@ -5,6 +5,12 @@ · 4 unmodified lines | |||
| 5 | #include <linux/gfp.h> | 5 | #include <linux/gfp.h> |
| 6 | #include <linux/export.h> | 6 | #include <linux/export.h> |
| 7 | 7 | ||
| 8 | enum devm_ioremap_type { | ||
| 9 | DEVM_IOREMAP = 0, | ||
| 10 | DEVM_IOREMAP_NC, | ||
| 11 | DEVM_IOREMAP_WC, | ||
| 12 | }; | ||
| 13 | |||
| 8 | void devm_ioremap_release(struct device *dev, void *res) | 14 | void devm_ioremap_release(struct device *dev, void *res) |
| 9 | { | 15 | { |
| 10 | iounmap(*(void __iomem **)res); | 16 | iounmap(*(void __iomem **)res); |
| @@ -15,24 +21,28 @@ static int devm_ioremap_match(struct device *dev, void *res, void *match_data) · 4 unmodified lines | |||
| 15 | return *(void **)res == match_data; | 21 | return *(void **)res == match_data; |
| 16 | } | 22 | } |
| 17 | 23 | ||
| 18 | /** | 24 | static void __iomem *__devm_ioremap(struct device *dev, resource_size_t offset, |
| 19 | * devm_ioremap - Managed ioremap() | 25 | resource_size_t size, |
| 20 | * @dev: Generic device to remap IO address for | 26 | enum devm_ioremap_type type) |
| 21 | * @offset: Resource address to map | ||
| 22 | * @size: Size of map | ||
| 23 | * | ||
| 24 | * Managed ioremap(). Map is automatically unmapped on driver detach. | ||
| 25 | */ | ||
| 26 | void __iomem *devm_ioremap(struct device *dev, resource_size_t offset, | ||
| 27 | resource_size_t size) | ||
| 28 | { | 27 | { |
| 29 | void __iomem **ptr, *addr; | 28 | void __iomem **ptr, *addr = NULL; |
| 30 | 29 | ||
| 31 | ptr = devres_alloc(devm_ioremap_release, sizeof(*ptr), GFP_KERNEL); | 30 | ptr = devres_alloc(devm_ioremap_release, sizeof(*ptr), GFP_KERNEL); |
| 32 | if (!ptr) | 31 | if (!ptr) |
| 33 | return NULL; | 32 | return NULL; |
| 34 | 33 | ||
| 35 | addr = ioremap(offset, size); | 34 | switch (type) { |
| 35 | case DEVM_IOREMAP: | ||
| 36 | addr = ioremap(offset, size); | ||
| 37 | break; | ||
| 38 | case DEVM_IOREMAP_NC: | ||
| 39 | addr = ioremap_nocache(offset, size); | ||
| 40 | break; | ||
| 41 | case DEVM_IOREMAP_WC: | ||
| 42 | addr = ioremap_wc(offset, size); | ||
| 43 | break; | ||
| 44 | } | ||
| 45 | |||
| 36 | if (addr) { | 46 | if (addr) { |
| 37 | *ptr = addr; | 47 | *ptr = addr; |
| 38 | devres_add(dev, ptr); | 48 | devres_add(dev, ptr); |
| @@ -41,6 +51,20 @@ void __iomem *devm_ioremap(struct device *dev, resource_size_t offset, · 2 unmodified lines | |||
| 41 | 51 | ||
| 42 | return addr; | 52 | return addr; |
| 43 | } | 53 | } |
| 54 | |||
| 55 | /** | ||
| 56 | * devm_ioremap - Managed ioremap() | ||
| 57 | * @dev: Generic device to remap IO address for | ||
| 58 | * @offset: Resource address to map | ||
| 59 | * @size: Size of map | ||
| 60 | * | ||
| 61 | * Managed ioremap(). Map is automatically unmapped on driver detach. | ||
| 62 | */ | ||
| 63 | void __iomem *devm_ioremap(struct device *dev, resource_size_t offset, | ||
| 64 | resource_size_t size) | ||
| 65 | { | ||
| 66 | return __devm_ioremap(dev, offset, size, DEVM_IOREMAP); | ||
| 67 | } | ||
| 44 | EXPORT_SYMBOL(devm_ioremap); | 68 | EXPORT_SYMBOL(devm_ioremap); |
| 45 | 69 | ||
| 46 | /** | 70 | /** |
| @@ -55,20 +79,7 @@ EXPORT_SYMBOL(devm_ioremap); · 8 unmodified lines | |||
| 55 | void __iomem *devm_ioremap_nocache(struct device *dev, resource_size_t offset, | 79 | void __iomem *devm_ioremap_nocache(struct device *dev, resource_size_t offset, |
| 56 | resource_size_t size) | 80 | resource_size_t size) |
| 57 | { | 81 | { |
| 58 | void __iomem **ptr, *addr; | 82 | return __devm_ioremap(dev, offset, size, DEVM_IOREMAP_NC); |
| 59 | |||
| 60 | ptr = devres_alloc(devm_ioremap_release, sizeof(*ptr), GFP_KERNEL); | ||
| 61 | if (!ptr) | ||
| 62 | return NULL; | ||
| 63 | |||
| 64 | addr = ioremap_nocache(offset, size); | ||
| 65 | if (addr) { | ||
| 66 | *ptr = addr; | ||
| 67 | devres_add(dev, ptr); | ||
| 68 | } else | ||
| 69 | devres_free(ptr); | ||
| 70 | |||
| 71 | return addr; | ||
| 72 | } | 83 | } |
| 73 | EXPORT_SYMBOL(devm_ioremap_nocache); | 84 | EXPORT_SYMBOL(devm_ioremap_nocache); |
| 74 | 85 | ||
| @@ -83,20 +94,7 @@ EXPORT_SYMBOL(devm_ioremap_nocache); · 8 unmodified lines | |||
| 83 | void __iomem *devm_ioremap_wc(struct device *dev, resource_size_t offset, | 94 | void __iomem *devm_ioremap_wc(struct device *dev, resource_size_t offset, |
| 84 | resource_size_t size) | 95 | resource_size_t size) |
| 85 | { | 96 | { |
| 86 | void __iomem **ptr, *addr; | 97 | return __devm_ioremap(dev, offset, size, DEVM_IOREMAP_WC); |
| 87 | |||
| 88 | ptr = devres_alloc(devm_ioremap_release, sizeof(*ptr), GFP_KERNEL); | ||
| 89 | if (!ptr) | ||
| 90 | return NULL; | ||
| 91 | |||
| 92 | addr = ioremap_wc(offset, size); | ||
| 93 | if (addr) { | ||
| 94 | *ptr = addr; | ||
| 95 | devres_add(dev, ptr); | ||
| 96 | } else | ||
| 97 | devres_free(ptr); | ||
| 98 | |||
| 99 | return addr; | ||
| 100 | } | 98 | } |
| 101 | EXPORT_SYMBOL(devm_ioremap_wc); | 99 | EXPORT_SYMBOL(devm_ioremap_wc); |
| 102 | 100 | ||
| @@ -21,15 +21,17 @@ · 20 unmodified lines | |||
| 21 | #include <stdio.h> | 21 | #include <stdio.h> |
| 22 | #include <stdlib.h> | 22 | #include <stdlib.h> |
| 23 | #include <unistd.h> | 23 | #include <unistd.h> |
| 24 | #include <string.h> | ||
| 24 | #include <errno.h> | 25 | #include <errno.h> |
| 25 | #include <linux/hyperv.h> | 26 | #include <linux/hyperv.h> |
| 27 | #include <linux/limits.h> | ||
| 26 | #include <syslog.h> | 28 | #include <syslog.h> |
| 27 | #include <sys/stat.h> | 29 | #include <sys/stat.h> |
| 28 | #include <fcntl.h> | 30 | #include <fcntl.h> |
| 29 | #include <getopt.h> | 31 | #include <getopt.h> |
| 30 | 32 | ||
| 31 | static int target_fd; | 33 | static int target_fd; |
| 32 | static char target_fname[W_MAX_PATH]; | 34 | static char target_fname[PATH_MAX]; |
| 33 | static unsigned long long filesize; | 35 | static unsigned long long filesize; |
| 34 | 36 | ||
| 35 | static int hv_start_fcopy(struct hv_start_fcopy *smsg) | 37 | static int hv_start_fcopy(struct hv_start_fcopy *smsg) |
| @@ -634,64 +634,6 @@ static char *kvp_if_name_to_mac(char *if_name) · 633 unmodified lines | |||
| 634 | return mac_addr; | 634 | return mac_addr; |
| 635 | } | 635 | } |
| 636 | 636 | ||
| 637 | |||
| 638 | /* | ||
| 639 | * Retrieve the interface name given tha MAC address. | ||
| 640 | */ | ||
| 641 | |||
| 642 | static char *kvp_mac_to_if_name(char *mac) | ||
| 643 | { | ||
| 644 | DIR *dir; | ||
| 645 | struct dirent *entry; | ||
| 646 | FILE *file; | ||
| 647 | char *p, *x; | ||
| 648 | char *if_name = NULL; | ||
| 649 | char buf[256]; | ||
| 650 | char dev_id[PATH_MAX]; | ||
| 651 | unsigned int i; | ||
| 652 | |||
| 653 | dir = opendir(KVP_NET_DIR); | ||
| 654 | if (dir == NULL) | ||
| 655 | return NULL; | ||
| 656 | |||
| 657 | while ((entry = readdir(dir)) != NULL) { | ||
| 658 | /* | ||
| 659 | * Set the state for the next pass. | ||
| 660 | */ | ||
| 661 | snprintf(dev_id, sizeof(dev_id), "%s%s/address", KVP_NET_DIR, | ||
| 662 | entry->d_name); | ||
| 663 | |||
| 664 | file = fopen(dev_id, "r"); | ||
| 665 | if (file == NULL) | ||
| 666 | continue; | ||
| 667 | |||
| 668 | p = fgets(buf, sizeof(buf), file); | ||
| 669 | if (p) { | ||
| 670 | x = strchr(p, '\n'); | ||
| 671 | if (x) | ||
| 672 | *x = '\0'; | ||
| 673 | |||
| 674 | for (i = 0; i < strlen(p); i++) | ||
| 675 | p[i] = toupper(p[i]); | ||
| 676 | |||
| 677 | if (!strcmp(p, mac)) { | ||
| 678 | /* | ||
| 679 | * Found the MAC match; return the interface | ||
| 680 | * name. The caller will free the memory. | ||
| 681 | */ | ||
| 682 | if_name = strdup(entry->d_name); | ||
| 683 | fclose(file); | ||
| 684 | break; | ||
| 685 | } | ||
| 686 | } | ||
| 687 | fclose(file); | ||
| 688 | } | ||
| 689 | |||
| 690 | closedir(dir); | ||
| 691 | return if_name; | ||
| 692 | } | ||
| 693 | |||
| 694 | |||
| 695 | static void kvp_process_ipconfig_file(char *cmd, | 637 | static void kvp_process_ipconfig_file(char *cmd, |
| 696 | char *config_buf, unsigned int len, | 638 | char *config_buf, unsigned int len, |
| 697 | int element_size, int offset) | 639 | int element_size, int offset) |
| @@ -997,6 +939,70 @@ getaddr_done: · 299 unmodified lines | |||
| 997 | return error; | 939 | return error; |
| 998 | } | 940 | } |
| 999 | 941 | ||
| 942 | /* | ||
| 943 | * Retrieve the IP given the MAC address. | ||
| 944 | */ | ||
| 945 | static int kvp_mac_to_ip(struct hv_kvp_ipaddr_value *kvp_ip_val) | ||
| 946 | { | ||
| 947 | char *mac = (char *)kvp_ip_val->adapter_id; | ||
| 948 | DIR *dir; | ||
| 949 | struct dirent *entry; | ||
| 950 | FILE *file; | ||
| 951 | char *p, *x; | ||
| 952 | char *if_name = NULL; | ||
| 953 | char buf[256]; | ||
| 954 | char dev_id[PATH_MAX]; | ||
| 955 | unsigned int i; | ||
| 956 | int error = HV_E_FAIL; | ||
| 957 | |||
| 958 | dir = opendir(KVP_NET_DIR); | ||
| 959 | if (dir == NULL) | ||
| 960 | return HV_E_FAIL; | ||
| 961 | |||
| 962 | while ((entry = readdir(dir)) != NULL) { | ||
| 963 | /* | ||
| 964 | * Set the state for the next pass. | ||
| 965 | */ | ||
| 966 | snprintf(dev_id, sizeof(dev_id), "%s%s/address", KVP_NET_DIR, | ||
| 967 | entry->d_name); | ||
| 968 | |||
| 969 | file = fopen(dev_id, "r"); | ||
| 970 | if (file == NULL) | ||
| 971 | continue; | ||
| 972 | |||
| 973 | p = fgets(buf, sizeof(buf), file); | ||
| 974 | fclose(file); | ||
| 975 | if (!p) | ||
| 976 | continue; | ||
| 977 | |||
| 978 | x = strchr(p, '\n'); | ||
| 979 | if (x) | ||
| 980 | *x = '\0'; | ||
| 981 | |||
| 982 | for (i = 0; i < strlen(p); i++) | ||
| 983 | p[i] = toupper(p[i]); | ||
| 984 | |||
| 985 | if (strcmp(p, mac)) | ||
| 986 | continue; | ||
| 987 | |||
| 988 | /* | ||
| 989 | * Found the MAC match. | ||
| 990 | * A NIC (e.g. VF) matching the MAC, but without IP, is skipped. | ||
| 991 | */ | ||
| 992 | if_name = entry->d_name; | ||
| 993 | if (!if_name) | ||
| 994 | continue; | ||
| 995 | |||
| 996 | error = kvp_get_ip_info(0, if_name, KVP_OP_GET_IP_INFO, | ||
| 997 | kvp_ip_val, MAX_IP_ADDR_SIZE * 2); | ||
| 998 | |||
| 999 | if (!error && strlen((char *)kvp_ip_val->ip_addr)) | ||
| 1000 | break; | ||
| 1001 | } | ||
| 1002 | |||
| 1003 | closedir(dir); | ||
| 1004 | return error; | ||
| 1005 | } | ||
| 1000 | 1006 | ||
| 1001 | static int expand_ipv6(char *addr, int type) | 1007 | static int expand_ipv6(char *addr, int type) |
| 1002 | { | 1008 | { |
| @@ -1472,26 +1478,12 @@ int main(int argc, char *argv[]) · 469 unmodified lines | |||
| 1472 | switch (op) { | 1478 | switch (op) { |
| 1473 | case KVP_OP_GET_IP_INFO: | 1479 | case KVP_OP_GET_IP_INFO: |
| 1474 | kvp_ip_val = &hv_msg->body.kvp_ip_val; | 1480 | kvp_ip_val = &hv_msg->body.kvp_ip_val; |
| 1475 | if_name = | ||
| 1476 | kvp_mac_to_if_name((char *)kvp_ip_val->adapter_id); | ||
| 1477 | 1481 | ||
| 1478 | if (if_name == NULL) { | 1482 | error = kvp_mac_to_ip(kvp_ip_val); |
| 1479 | /* | ||
| 1480 | * We could not map the mac address to an | ||
| 1481 | * interface name; return error. | ||
| 1482 | */ | ||
| 1483 | hv_msg->error = HV_E_FAIL; | ||
| 1484 | break; | ||
| 1485 | } | ||
| 1486 | error = kvp_get_ip_info( | ||
| 1487 | 0, if_name, KVP_OP_GET_IP_INFO, | ||
| 1488 | kvp_ip_val, | ||
| 1489 | (MAX_IP_ADDR_SIZE * 2)); | ||
| 1490 | 1483 | ||
| 1491 | if (error) | 1484 | if (error) |
| 1492 | hv_msg->error = error; | 1485 | hv_msg->error = error; |
| 1493 | 1486 | ||
| 1494 | free(if_name); | ||
| 1495 | break; | 1487 | break; |
| 1496 | 1488 | ||
| 1497 | case KVP_OP_SET_IP_INFO: | 1489 | case KVP_OP_SET_IP_INFO: |
| @@ -22,6 +22,7 @@ · 21 unmodified lines | |||
| 22 | #include <sys/poll.h> | 22 | #include <sys/poll.h> |
| 23 | #include <sys/ioctl.h> | 23 | #include <sys/ioctl.h> |
| 24 | #include <sys/stat.h> | 24 | #include <sys/stat.h> |
| 25 | #include <sys/sysmacros.h> | ||
| 25 | #include <fcntl.h> | 26 | #include <fcntl.h> |
| 26 | #include <stdio.h> | 27 | #include <stdio.h> |
| 27 | #include <mntent.h> | 28 | #include <mntent.h> |